refs #413 Breaking circular dependencies by moving mixin classes from valueobject.h to the files where they fit best:

* DBus mixins moved to dbus.h
* Json mixins moved to json.h
* Icon mixin moved to icon.h
* Comparison mixins moved to compare.h
* Hash and string mixins moved to blackmiscfreefunctions.h
* Index mixin moved to propertyindexvariantmap.h
* MetaType mixins moved to variant.h
* registerMetaValueType moved to variant.h
* valueobject_private.h renamed to variant_private.h
This commit is contained in:
Mathew Sutcliffe
2015-05-05 00:54:08 +01:00
parent 9cdacf8d86
commit 5a91d761c2
23 changed files with 749 additions and 668 deletions

View File

@@ -16,6 +16,7 @@
#include "blackmisc/icons.h"
#include "blackmisc/worker.h"
#include "blackmisc/variant.h"
#include "blackmisc/propertyindex.h"
#include <QTableView>
#include <QWizardPage>
#include <QHeaderView>

View File

@@ -10,6 +10,7 @@
//! \file
#include "blackmisc/blackmiscexport.h"
#include "blackmisc/valueobject.h"
#include "blackmisc/propertyindex.h"
#include "blackmisc/blackmiscfreefunctions.h"
#include <QString>

View File

@@ -13,6 +13,7 @@
#define BLACKMISC_AVIATION_AIRCRAFTICAO_H
#include "blackmisc/blackmiscexport.h"
#include "blackmisc/valueobject.h"
#include "blackmisc/propertyindex.h"
#include "blackmisc/blackmiscfreefunctions.h"

View File

@@ -76,7 +76,7 @@ namespace BlackMisc
switch (i)
{
case IndexEngines:
this->m_engines.setPropertyByIndex(variant, index.copyFrontRemoved());
this->m_engines = variant.to<decltype(this->m_engines)>();
break;
case IndexFlapsPercentage:
this->m_flapsPercentage = variant.toInt();

View File

@@ -13,6 +13,7 @@
#define BLACKMISC_AVIATION_CALLSIGN_H
#include "blackmisc/blackmiscexport.h"
#include "blackmisc/valueobject.h"
#include "blackmisc/propertyindex.h"
#include "blackmisc/icon.h"
#include "blackmisc/blackmiscfreefunctions.h"

View File

@@ -13,6 +13,8 @@
#define BLACKMISC_FREEFUNCTIONS_H
#include "blackmisc/blackmiscexport.h"
#include "blackmisc/tuple.h"
#include "blackmisc/inheritance_traits.h"
#include <QDir> // for Q_INIT_RESOURCE
#include <QList>
#include <QMap>
@@ -111,6 +113,103 @@ namespace BlackMisc
//! Init resources
BLACKMISC_EXPORT void initResources();
namespace Mixin
{
/*!
* CRTP class template from which a derived class can inherit common methods dealing with hashing instances by metatuple.
*/
template <class Derived>
class HashByTuple : private Private::EncapsulationBreaker
{
public:
//! qHash overload, needed for storing value in a QSet.
friend uint qHash(const Derived &value, uint seed = 0)
{
return ::qHash(hashImpl(value), seed);
}
private:
static uint hashImpl(const Derived &value)
{
return BlackMisc::qHash(toMetaTuple(value)) ^ baseHash(static_cast<const BaseOfT<Derived> *>(&value));
}
template <typename T> static uint baseHash(const T *base) { return qHash(*base); }
static uint baseHash(const void *) { return 0; }
};
/*!
* CRTP class template from which a derived class can inherit string streaming operations.
*/
template <class Derived>
class String
{
public:
//! Stream << overload to be used in debugging messages
friend QDebug operator<<(QDebug debug, const Derived &obj)
{
debug << obj.stringForStreaming();
return debug;
}
//! Operator << when there is no debug stream
friend QNoDebug operator<<(QNoDebug nodebug, const Derived &obj)
{
Q_UNUSED(obj);
return nodebug;
}
//! Operator << based on text stream
friend QTextStream &operator<<(QTextStream &stream, const Derived &obj)
{
stream << obj.stringForStreaming();
return stream;
}
//! Operator << for QDataStream
friend QDataStream &operator<<(QDataStream &stream, const Derived &obj)
{
stream << obj.stringForStreaming();
return stream;
}
//! Stream operator << for std::cout
friend std::ostream &operator<<(std::ostream &ostr, const Derived &obj)
{
ostr << obj.stringForStreaming().toStdString();
return ostr;
}
//! Cast as QString
QString toQString(bool i18n = false) const { return derived()->convertToQString(i18n); }
//! Cast to pretty-printed QString
QString toFormattedQString(bool i18n = false) const { return derived()->toQString(i18n); }
//! To std string
std::string toStdString(bool i18n = false) const { return derived()->convertToQString(i18n).toStdString(); }
//! String for streaming operators
QString stringForStreaming() const { return derived()->convertToQString(); }
private:
const Derived *derived() const { return static_cast<const Derived *>(this); }
Derived *derived() { return static_cast<Derived *>(this); }
};
/*!
* When a derived class and a base class both inherit from Mixin::String,
* the derived class uses this macro to disambiguate the inherited members.
*/
# define BLACKMISC_DECLARE_USING_MIXIN_STRING(DERIVED) \
using ::BlackMisc::Mixin::String<DERIVED>::toQString; \
using ::BlackMisc::Mixin::String<DERIVED>::toFormattedQString; \
using ::BlackMisc::Mixin::String<DERIVED>::toStdString; \
using ::BlackMisc::Mixin::String<DERIVED>::stringForStreaming;
} // Mixin
//! Checked version from QVariant
template <class T> void setFromQVariant(T *value, const QVariant &variant)
{

View File

@@ -9,8 +9,6 @@
//! \file
#include "valueobject.h" // outside include guard due to cyclic dependency hack (MS)
#ifndef BLACKMISC_COLLECTION_H
#define BLACKMISC_COLLECTION_H

132
src/blackmisc/compare.h Normal file
View File

@@ -0,0 +1,132 @@
/* Copyright (C) 2015
* swift project Community / Contributors
*
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
* directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project,
* including this file, may be copied, modified, propagated, or distributed except according to the terms
* contained in the LICENSE file.
*/
//! \file
#ifndef BLACKMISC_COMPARE_H
#define BLACKMISC_COMPARE_H
#include "blackmisc/tuple.h"
#include "blackmisc/inheritance_traits.h"
namespace BlackMisc
{
namespace Mixin
{
/*!
* CRTP class template from which a derived class can inherit operator== implemented using its compare function.
*/
template <class Derived>
class EqualsByCompare
{
public:
//! Equals
friend bool operator ==(const Derived &a, const Derived &b) { return compare(a, b) == 0; }
//! Not equal
friend bool operator !=(const Derived &a, const Derived &b) { return compare(a, b) != 0; }
};
/*!
* CRTP class template from which a derived class can inherit operator== implemented by metatuple.
*/
template <class Derived>
class EqualsByTuple : private Private::EncapsulationBreaker
{
public:
//! Equals
friend bool operator ==(const Derived &a, const Derived &b) { return equals(a, b); }
//! Not equal
friend bool operator !=(const Derived &a, const Derived &b) { return ! equals(a, b); }
private:
static bool equals(const Derived &a, const Derived &b)
{
return toMetaTuple(a) == toMetaTuple(b) && baseEquals(static_cast<const BaseOfT<Derived> *>(&a), static_cast<const BaseOfT<Derived> *>(&b));
}
template <typename T> static bool baseEquals(const T *a, const T *b) { return *a == *b; }
static bool baseEquals(const void *, const void *) { return true; }
};
/*!
* CRTP class template from which a derived class can inherit operator< implemented using its compare function.
*/
template <class Derived>
class LessThanByCompare
{
public:
//! Less than
friend bool operator <(const Derived &a, const Derived &b) { return compare(a, b) < 0; }
//! Greater than
friend bool operator >(const Derived &a, const Derived &b) { return compare(a, b) > 0; }
//! Less than or equal
friend bool operator <=(const Derived &a, const Derived &b) { return compare(a, b) <= 0; }
//! Greater than or equal
friend bool operator >=(const Derived &a, const Derived &b) { return compare(a, b) >= 0; }
};
/*!
* CRTP class template from which a derived class can inherit operator< implemented by metatuple.
*/
template <class Derived>
class LessThanByTuple : private Private::EncapsulationBreaker
{
public:
//! Less than
friend bool operator <(const Derived &a, const Derived &b) { return less(a, b); }
//! Greater than
friend bool operator >(const Derived &a, const Derived &b) { return less(b, a); }
//! Less than or equal
friend bool operator <=(const Derived &a, const Derived &b) { return ! less(b, a); }
//! Greater than or equal
friend bool operator >=(const Derived &a, const Derived &b) { return ! less(a, b); }
private:
static bool less(const Derived &a, const Derived &b)
{
if (baseLess(static_cast<const BaseOfT<Derived> *>(&a), static_cast<const BaseOfT<Derived> *>(&b))) { return true; }
return toMetaTuple(a) < toMetaTuple(b);
}
template <typename T> static bool baseLess(const T *a, const T *b) { return *a < *b; }
static bool baseLess(const void *, const void *) { return false; }
};
/*!
* CRTP class template from which a derived class can inherit non-member compare() implemented by metatuple.
*/
template <class Derived>
class CompareByTuple : private Private::EncapsulationBreaker
{
public:
//! Return negative, zero, or positive if a is less than, equal to, or greater than b.
friend int compare(const Derived &a, const Derived &b) { return compareImpl(a, b); }
private:
static int compareImpl(const Derived &a, const Derived &b)
{
int baseCmp = baseCompare(static_cast<const BaseOfT<Derived> *>(&a), static_cast<const BaseOfT<Derived> *>(&b));
if (baseCmp) { return baseCmp; }
return BlackMisc::compare(toMetaTuple(a), toMetaTuple(b));
}
template <typename T> static int baseCompare(const T *a, const T *b) { return compare(*a, *b); }
static int baseCompare(const void *, const void *) { return 0; }
};
} // Mixin
} // BlackMisc
#endif

View File

@@ -16,6 +16,9 @@
#include "blackmiscfreefunctions.h"
#include "predicates.h"
#include "json.h"
#include "variant.h"
#include "dbus.h"
#include "icon.h"
#include <algorithm>
namespace BlackMisc
@@ -51,7 +54,6 @@ namespace BlackMisc
public Mixin::DBusOperators<C<T>>,
public Mixin::JsonOperators<C<T>>,
public Mixin::String<C<T>>,
public Mixin::Index<C<T>>,
public Mixin::Icon<C<T>>
{
public:

View File

@@ -12,9 +12,86 @@
#ifndef BLACKMISC_DBUS_H
#define BLACKMISC_DBUS_H
#include "blackmisc/tuple.h"
#include "blackmisc/inheritance_traits.h"
#include <QDBusArgument>
#include <type_traits>
namespace BlackMisc
{
namespace Mixin
{
/*!
* CRTP class template which will generate marshalling operators for a derived class with its own marshalling implementation.
*/
template <class Derived>
class DBusOperators
{
public:
//! Unmarshalling operator >>, DBus to object
friend const QDBusArgument &operator>>(const QDBusArgument &arg, Derived &obj)
{
arg.beginStructure();
obj.unmarshallFromDbus(arg);
arg.endStructure();
return arg;
}
//! Marshalling operator <<, object to DBus
friend QDBusArgument &operator<<(QDBusArgument &arg, const Derived &obj)
{
arg.beginStructure();
obj.marshallToDbus(arg);
arg.endStructure();
return arg;
}
};
/*!
* CRTP class template from which a derived class can inherit common methods dealing with marshalling instances by metatuple.
*/
template <class Derived>
class DBusByTuple : public DBusOperators<Derived>, private Private::EncapsulationBreaker
{
public:
//! Marshall without begin/endStructure, for when composed within another object
void marshallToDbus(QDBusArgument &arg) const
{
baseMarshall(static_cast<const BaseOfT<Derived> *>(derived()), arg);
using BlackMisc::operator<<;
arg << Private::EncapsulationBreaker::toMetaTuple(*derived());
}
//! Unmarshall without begin/endStructure, for when composed within another object
void unmarshallFromDbus(const QDBusArgument &arg)
{
baseUnmarshall(static_cast<BaseOfT<Derived> *>(derived()), arg);
using BlackMisc::operator>>;
arg >> Private::EncapsulationBreaker::toMetaTuple(*derived());
}
private:
const Derived *derived() const { return static_cast<const Derived *>(this); }
Derived *derived() { return static_cast<Derived *>(this); }
template <typename T> static void baseMarshall(const T *base, QDBusArgument &arg) { base->marshallToDbus(arg); }
template <typename T> static void baseUnmarshall(T *base, const QDBusArgument &arg) { base->unmarshallFromDbus(arg); }
static void baseMarshall(const void *, QDBusArgument &) {}
static void baseUnmarshall(void *, const QDBusArgument &) {}
};
/*!
* When a derived class and a base class both inherit from Mixin::DBusByTuple,
* the derived class uses this macro to disambiguate the inherited members.
*/
# define BLACKMISC_DECLARE_USING_MIXIN_DBUS(DERIVED) \
using ::BlackMisc::Mixin::DBusByTuple<DERIVED>::marshallToDbus; \
using ::BlackMisc::Mixin::DBusByTuple<DERIVED>::unmarshallFromDbus;
} // Mixin
} // BlackMisc
/*!
* Non-member non-friend operator for streaming enums to QDBusArgument.
*
@@ -31,4 +108,5 @@ operator>>(const QDBusArgument &argument, ENUM &enumType)
enumType = static_cast<ENUM>(e);
return argument;
}
#endif // guard

View File

@@ -14,6 +14,7 @@
#include "blackmisc/blackmiscexport.h"
#include "blackmisc/propertyindex.h"
#include "blackmisc/valueobject.h"
#include "blackmisc/blackmiscfreefunctions.h"
#include <QStringList>
#include <QKeySequence>

View File

@@ -14,6 +14,7 @@
#include "blackmiscexport.h"
#include "propertyindex.h"
#include "valueobject.h"
#include "blackmiscfreefunctions.h"
namespace BlackMisc

View File

@@ -9,25 +9,66 @@
//! \file
#include "valueobject.h" // outside include guard due to cyclic dependency hack (MS)
#ifndef BLACKMISC_ICON_H
#define BLACKMISC_ICON_H
#include "blackmiscexport.h"
#include "icons.h"
#include "variant.h"
#include "tuple.h"
#include "inheritance_traits.h"
#include <QIcon>
namespace BlackMisc
{
class CIcon;
namespace PhysicalQuantities
{
class CAngle;
}
namespace Mixin
{
/*!
* CRTP class template from which a derived class can inherit icon-related functions.
*/
template <class Derived, CIcons::IconIndex IconIndex = CIcons::StandardIconUnknown16>
class Icon
{
public:
//! As icon, not implemented by all classes
CIcon toIcon() const;
//! As pixmap, required for most GUI views
QPixmap toPixmap() const;
private:
const Derived *derived() const { return static_cast<const Derived *>(this); }
Derived *derived() { return static_cast<Derived *>(this); }
};
/*!
* When a derived class and a base class both inherit from Mixin::Icon,
* the derived class uses this macro to disambiguate the inherited members.
*/
# define BLACKMISC_DECLARE_USING_MIXIN_ICON(DERIVED) \
using ::BlackMisc::Mixin::Icon<DERIVED>::toIcon; \
using ::BlackMisc::Mixin::Icon<DERIVED>::toPixmap;
} // Mixin
//! Value object for icons. An icon is stored in the global icon repository and
//! identified by its index. It contains no(!) pyhsical data for the icon itself.
class BLACKMISC_EXPORT CIcon : public CValueObject<CIcon>
class BLACKMISC_EXPORT CIcon :
public Mixin::MetaType<CIcon>,
public Mixin::HashByTuple<CIcon>,
public Mixin::DBusByTuple<CIcon>,
public Mixin::JsonByTuple<CIcon>,
public Mixin::EqualsByTuple<CIcon>,
public Mixin::LessThanByTuple<CIcon>,
public Mixin::CompareByTuple<CIcon>,
public Mixin::String<CIcon>,
public Mixin::Icon<CIcon>
{
public:
//! Default constructor.
@@ -85,6 +126,20 @@ namespace BlackMisc
//! \private Needed so we can copy forward-declared CIcon.
inline void assign(CIcon &a, const CIcon &b) { a = b; }
}
namespace Mixin
{
template <class Derived, CIcons::IconIndex IconIndex>
CIcon Icon<Derived, IconIndex>::toIcon() const
{
return CIcon::iconByIndex(IconIndex);
}
template <class Derived, CIcons::IconIndex IconIndex>
QPixmap Icon<Derived, IconIndex>::toPixmap() const
{
return derived()->toIcon().toPixmap();
}
}
} // namespace
BLACK_DECLARE_TUPLE_CONVERSION(BlackMisc::CIcon, (o.m_index, o.m_descriptiveText))

View File

@@ -9,8 +9,6 @@
//! \file
#include "valueobject.h" // outside include guard due to cyclic dependency hack (MS)
#ifndef BLACKMISC_ICONLIST_H
#define BLACKMISC_ICONLIST_H

View File

@@ -14,7 +14,9 @@
#ifndef BLACKMISC_JSON_H
#define BLACKMISC_JSON_H
#include "blackmiscexport.h"
#include "blackmisc/blackmiscexport.h"
#include "blackmisc/tuple.h"
#include "blackmisc/inheritance_traits.h"
#include <QJsonObject>
#include <QJsonValue>
#include <QJsonValueRef>
@@ -113,6 +115,7 @@ namespace BlackMisc
{
namespace Json
{
//! \brief Append to first JSON object (concatenate)
//! \ingroup JSON
inline QJsonObject &appendJsonObject(QJsonObject &target, const QJsonObject &toBeAppended)
@@ -125,7 +128,95 @@ namespace BlackMisc
}
return target;
}
} // ns
} // ns
} // Json
namespace Mixin
{
/*!
* CRTP class template which will generate marshalling operators for a derived class with its own marshalling implementation.
*/
template <class Derived>
class JsonOperators
{
public:
//! operator >> for JSON
friend const QJsonObject &operator>>(const QJsonObject &json, Derived &obj)
{
obj.convertFromJson(json);
return json;
}
//! operator >> for JSON
friend const QJsonValue &operator>>(const QJsonValue &json, Derived &obj)
{
obj.convertFromJson(json.toObject());
return json;
}
//! operator >> for JSON
friend const QJsonValueRef &operator>>(const QJsonValueRef &json, Derived &obj)
{
obj.convertFromJson(json.toObject());
return json;
}
//! operator << for JSON
friend QJsonArray &operator<<(QJsonArray &json, const Derived &obj)
{
json.append(obj.toJson());
return json;
}
//! operator << for JSON
friend QJsonObject &operator<<(QJsonObject &json, const std::pair<QString, const Derived &> &value)
{
json.insert(value.first, QJsonValue(value.second.toJson()));
return json;
}
};
/*!
* CRTP class template from which a derived class can inherit common methods dealing with JSON by metatuple.
*/
template <class Derived>
class JsonByTuple : public JsonOperators<Derived>, private Private::EncapsulationBreaker
{
public:
//! Cast to JSON object
QJsonObject toJson() const
{
QJsonObject json = BlackMisc::serializeJson(Private::EncapsulationBreaker::toMetaTuple(*derived()));
return Json::appendJsonObject(json, baseToJson(static_cast<const BaseOfT<Derived> *>(derived())));
}
//! Assign from JSON object
void convertFromJson(const QJsonObject &json)
{
baseConvertFromJson(static_cast<BaseOfT<Derived> *>(derived()), json);
BlackMisc::deserializeJson(json, Private::EncapsulationBreaker::toMetaTuple(*derived()));
}
private:
const Derived *derived() const { return static_cast<const Derived *>(this); }
Derived *derived() { return static_cast<Derived *>(this); }
template <typename T> static QJsonObject baseToJson(const T *base) { return base->toJson(); }
template <typename T> static void baseConvertFromJson(T *base, const QJsonObject &json) { base->convertFromJson(json); }
static QJsonObject baseToJson(const void *) { return {}; }
static void baseConvertFromJson(void *, const QJsonObject &) {}
};
/*!
* When a derived class and a base class both inherit from Mixin::JsonByTuple,
* the derived class uses this macro to disambiguate the inherited members.
*/
# define BLACKMISC_DECLARE_USING_MIXIN_JSON(DERIVED) \
using ::BlackMisc::Mixin::JsonByTuple<DERIVED>::toJson; \
using ::BlackMisc::Mixin::JsonByTuple<DERIVED>::convertFromJson;
} // Mixin
} // BlackMisc
#endif // guard

View File

@@ -13,6 +13,7 @@
//! \file
#include "blackmiscexport.h"
#include "valueobject.h"
#include "sequence.h"
namespace BlackMisc

View File

@@ -9,13 +9,15 @@
//! \file
#include "valueobject.h" // outside include guard due to cyclic dependency hack (MS)
#ifndef BLACKMISC_PROPERTYINDEX_H
#define BLACKMISC_PROPERTYINDEX_H
#include "blackmiscexport.h"
#include "blackmiscfreefunctions.h"
#include "variant.h"
#include "dbus.h"
#include "json.h"
#include "compare.h"
#include <initializer_list>
namespace BlackMisc
@@ -25,7 +27,15 @@ namespace BlackMisc
* Property index. The index can be nested, that's why it is a sequence
* (e.g. PropertyIndexPilot, PropertyIndexRealname).
*/
class BLACKMISC_EXPORT CPropertyIndex : public CValueObject<CPropertyIndex>
class BLACKMISC_EXPORT CPropertyIndex :
public Mixin::MetaType<CPropertyIndex>,
public Mixin::HashByTuple<CPropertyIndex>,
public Mixin::DBusByTuple<CPropertyIndex>,
public Mixin::JsonByTuple<CPropertyIndex>,
public Mixin::EqualsByTuple<CPropertyIndex>,
public Mixin::LessThanByTuple<CPropertyIndex>,
public Mixin::CompareByTuple<CPropertyIndex>,
public Mixin::String<CPropertyIndex>
{
// In the first trial I have used CSequence<int> as base class
// This has created too much circular dependencies of the headers

View File

@@ -9,8 +9,6 @@
//! \file
#include "valueobject.h" // outside include guard due to cyclic dependency hack (MS)
#ifndef BLACKMISC_PROPERTYINDEXLIST_H
#define BLACKMISC_PROPERTYINDEXLIST_H

View File

@@ -13,15 +13,57 @@
#define BLACKMISC_PROPERTYINDEXVARIANTMAP_H
#include "variant.h"
#include "valueobject.h"
#include "propertyindexlist.h"
#include "blackmiscexport.h"
#include "tuple.h"
#include "inheritance_traits.h"
#include <QVariantMap>
#include <QDBusArgument>
namespace BlackMisc
{
class CPropertyIndexVariantMap;
namespace Mixin
{
/*!
* CRTP class template from which a derived class can inherit property indexing functions.
*/
template <class Derived>
class Index
{
public:
//! Base class enums
enum ColumnIndex
{
IndexPixmap = 10, // manually set to avoid circular dependencies
IndexIcon,
IndexString
};
//! Update by variant map
//! \return number of values changed, with skipEqualValues equal values will not be changed
CPropertyIndexList apply(const BlackMisc::CPropertyIndexVariantMap &indexMap, bool skipEqualValues = false);
//! Set property by index
void setPropertyByIndex(const CVariant &variant, const CPropertyIndex &index);
//! Property by index
CVariant propertyByIndex(const BlackMisc::CPropertyIndex &index) const;
//! Property by index as String
QString propertyByIndexAsString(const CPropertyIndex &index, bool i18n = false) const;
//! Is given variant equal to value of property index?
bool equalsPropertyByIndex(const CVariant &compareValue, const CPropertyIndex &index) const;
private:
const Derived *derived() const { return static_cast<const Derived *>(this); }
Derived *derived() { return static_cast<Derived *>(this); }
};
} // Mixin
/*!
* Specialized value object compliant map for variants,
* based on indexes
@@ -29,9 +71,7 @@ namespace BlackMisc
class BLACKMISC_EXPORT CPropertyIndexVariantMap :
public Mixin::MetaType<CPropertyIndexVariantMap>,
public Mixin::DBusOperators<CPropertyIndexVariantMap>,
public Mixin::Index<CPropertyIndexVariantMap>,
public Mixin::String<CPropertyIndexVariantMap>,
public Mixin::Icon<CPropertyIndexVariantMap>
public Mixin::String<CPropertyIndexVariantMap>
{
public:
/*!
@@ -155,6 +195,81 @@ namespace BlackMisc
//! \copydoc CValueObject::unmarshallFromDbus
void unmarshallFromDbus(const QDBusArgument &argument);
};
namespace Mixin
{
template <class Derived>
CPropertyIndexList Index<Derived>::apply(const BlackMisc::CPropertyIndexVariantMap &indexMap, bool skipEqualValues)
{
if (indexMap.isEmpty()) return {};
CPropertyIndexList changed;
const auto &map = indexMap.map();
for (auto it = map.begin(); it != map.end(); ++it)
{
const CVariant value = it.value().toCVariant();
const CPropertyIndex index = it.key();
if (skipEqualValues)
{
bool equal = derived()->equalsPropertyByIndex(value, index);
if (equal) { continue; }
}
derived()->setPropertyByIndex(value, index);
changed.push_back(index);
}
return changed;
}
template <class Derived>
void Index<Derived>::setPropertyByIndex(const CVariant &variant, const CPropertyIndex &index)
{
if (index.isMyself())
{
derived()->convertFromCVariant(variant);
return;
}
// not all classes have implemented nesting
const QString m = QString("Property by index not found (setter), index: ").append(index.toQString());
qFatal("%s", qPrintable(m));
}
template <class Derived>
CVariant Index<Derived>::propertyByIndex(const CPropertyIndex &index) const
{
if (index.isMyself())
{
return derived()->toCVariant();
}
auto i = index.frontCasted<ColumnIndex>();
switch (i)
{
case IndexIcon:
return CVariant::from(derived()->toIcon());
case IndexPixmap:
return CVariant::from(derived()->toPixmap());
case IndexString:
return CVariant(derived()->toQString());
default:
break;
}
// not all classes have implemented nesting
const QString m = QString("Property by index not found, index: ").append(index.toQString());
qFatal("%s", qPrintable(m));
return {};
}
template <class Derived>
QString Index<Derived>::propertyByIndexAsString(const CPropertyIndex &index, bool i18n) const
{
// default implementation, requires propertyByIndex
return derived()->propertyByIndex(index).toQString(i18n);
}
template <class Derived>
bool Index<Derived>::equalsPropertyByIndex(const CVariant &compareValue, const CPropertyIndex &index) const
{
return derived()->propertyByIndex(index) == compareValue;
}
} // Mixin
}
Q_DECLARE_METATYPE(BlackMisc::CPropertyIndexVariantMap)

View File

@@ -9,8 +9,6 @@
//! \file
#include "valueobject.h" // outside include guard due to cyclic dependency hack (MS)
#ifndef BLACKMISC_SEQUENCE_H
#define BLACKMISC_SEQUENCE_H

View File

@@ -16,9 +16,11 @@
#include "dbus.h"
#include "tuple.h"
#include "json.h"
#include "icons.h"
#include "compare.h"
#include "variant.h"
#include "propertyindexvariantmap.h"
#include "iconlist.h"
#include "blackmiscfreefunctions.h"
#include "valueobject_private.h"
#include <QtDBus/QDBusMetaType>
#include <QString>
#include <QtGlobal>
@@ -33,30 +35,6 @@
namespace BlackMisc
{
class CPropertyIndex;
class CPropertyIndexList;
class CPropertyIndexVariantMap;
class CIcon;
class CVariant;
class CEmpty;
/*!
* This registers the value type T with the BlackMisc meta type system,
* making it available for use with the extended feature set of BlackMisc::CVariant.
*
* The implementation (ab)uses the QMetaType converter function registration mechanism
* to store a type-erased representation of the set of operations supported by T.
* Unlike the singleton pattern, this approach means that CVariant can be used in plugins.
*/
template <typename T>
void registerMetaValueType()
{
if (QMetaType::hasRegisteredConverterFunction<T, Private::IValueObjectMetaInfo *>()) { return; }
auto converter = [](const T &) { static Private::CValueObjectMetaInfo<T> info; return &info; };
bool ok = QMetaType::registerConverter<T, Private::IValueObjectMetaInfo *>(converter);
Q_ASSERT(ok);
Q_UNUSED(ok);
}
/*!
* Default base class for CValueObject.
@@ -81,512 +59,6 @@ namespace BlackMisc
~CEmpty() = default;
};
namespace Mixin
{
/*!
* CRTP class template from which a derived class can inherit common methods dealing with the metatype of the class.
*/
template <class Derived, class... AdditionalTypes>
class MetaType
{
public:
//! Register metadata
static void registerMetadata()
{
Private::MetaTypeHelper<Derived>::maybeRegisterMetaType();
[](...){}((qRegisterMetaType<AdditionalTypes>(), qDBusRegisterMetaType<AdditionalTypes>(), 0)...);
}
//! Returns the Qt meta type ID of this object.
int getMetaTypeId() const
{
return Private::MetaTypeHelper<Derived>::maybeGetMetaTypeId();
}
//! Returns true if this object is an instance of the class with the given meta type ID, or one of its subclasses.
bool isA(int metaTypeId) const
{
if (metaTypeId == QMetaType::UnknownType) { return false; }
if (metaTypeId == getMetaTypeId()) { return true; }
return baseIsA(static_cast<const MetaBaseOfT<Derived> *>(derived()), metaTypeId);
}
//! Method to return CVariant
//! \deprecated Use CVariant::to() instead.
CVariant toCVariant() const;
//! Set from CVariant
//! \deprecated Use CVariant::from() instead.
void convertFromCVariant(const CVariant &variant);
//! Return QVariant, used with DBus QVariant lists
//! \deprecated Use QVariant::fromValue() instead.
QVariant toQVariant() const
{
return Private::MetaTypeHelper<Derived>::maybeToQVariant(*derived());
}
//! Set from QVariant
//! \deprecated Use QVariant::value() instead.
void convertFromQVariant(const QVariant &variant)
{
return Private::MetaTypeHelper<Derived>::maybeConvertFromQVariant(*derived(), variant);
}
private:
const Derived *derived() const { return static_cast<const Derived *>(this); }
Derived *derived() { return static_cast<Derived *>(this); }
template <typename Base2> static bool baseIsA(const Base2 *base, int metaTypeId) { return base->isA(metaTypeId); }
static bool baseIsA(const void *, int) { return false; }
};
/*!
* Variant of MetaType mixin which also registers QList<Derived> with the type system.
*/
template <class Derived>
class MetaTypeAndQList : public MetaType<Derived, QList<Derived>>
{};
/*!
* When a derived class and a base class both inherit from Mixin::MetaType,
* the derived class uses this macro to disambiguate the inherited members.
*/
# define BLACKMISC_DECLARE_USING_MIXIN_METATYPE(DERIVED) \
using ::BlackMisc::Mixin::MetaType<DERIVED>::registerMetadata; \
using ::BlackMisc::Mixin::MetaType<DERIVED>::getMetaTypeId; \
using ::BlackMisc::Mixin::MetaType<DERIVED>::isA; \
using ::BlackMisc::Mixin::MetaType<DERIVED>::toCVariant; \
using ::BlackMisc::Mixin::MetaType<DERIVED>::toQVariant; \
using ::BlackMisc::Mixin::MetaType<DERIVED>::convertFromCVariant; \
using ::BlackMisc::Mixin::MetaType<DERIVED>::convertFromQVariant;
/*!
* When a derived class and a base class both inherit from Mixin::MetaType,
* the derived class uses this macro to disambiguate the inherited members.
*/
# define BLACKMISC_DECLARE_USING_MIXIN_METATYPE_AND_QLIST(DERIVED) \
using ::BlackMisc::Mixin::MetaTypeAndQList<DERIVED>::registerMetadata; \
using ::BlackMisc::Mixin::MetaTypeAndQList<DERIVED>::getMetaTypeId; \
using ::BlackMisc::Mixin::MetaTypeAndQList<DERIVED>::isA; \
using ::BlackMisc::Mixin::MetaTypeAndQList<DERIVED>::toCVariant; \
using ::BlackMisc::Mixin::MetaTypeAndQList<DERIVED>::toQVariant; \
using ::BlackMisc::Mixin::MetaTypeAndQList<DERIVED>::convertFromCVariant; \
using ::BlackMisc::Mixin::MetaTypeAndQList<DERIVED>::convertFromQVariant;
/*!
* CRTP class template from which a derived class can inherit common methods dealing with hashing instances by metatuple.
*/
template <class Derived>
class HashByTuple : private Private::EncapsulationBreaker
{
public:
//! qHash overload, needed for storing value in a QSet.
friend uint qHash(const Derived &value, uint seed = 0)
{
return ::qHash(hashImpl(value), seed);
}
private:
static uint hashImpl(const Derived &value)
{
return BlackMisc::qHash(toMetaTuple(value)) ^ baseHash(static_cast<const BaseOfT<Derived> *>(&value));
}
template <typename T> static uint baseHash(const T *base) { return qHash(*base); }
static uint baseHash(const void *) { return 0; }
};
/*!
* CRTP class template which will generate marshalling operators for a derived class with its own marshalling implementation.
*/
template <class Derived>
class DBusOperators
{
public:
//! Unmarshalling operator >>, DBus to object
friend const QDBusArgument &operator>>(const QDBusArgument &arg, Derived &obj)
{
arg.beginStructure();
obj.unmarshallFromDbus(arg);
arg.endStructure();
return arg;
}
//! Marshalling operator <<, object to DBus
friend QDBusArgument &operator<<(QDBusArgument &arg, const Derived &obj)
{
arg.beginStructure();
obj.marshallToDbus(arg);
arg.endStructure();
return arg;
}
};
/*!
* CRTP class template from which a derived class can inherit common methods dealing with marshalling instances by metatuple.
*/
template <class Derived>
class DBusByTuple : public DBusOperators<Derived>, private Private::EncapsulationBreaker
{
public:
//! Marshall without begin/endStructure, for when composed within another object
void marshallToDbus(QDBusArgument &arg) const
{
baseMarshall(static_cast<const BaseOfT<Derived> *>(derived()), arg);
using BlackMisc::operator<<;
arg << Private::EncapsulationBreaker::toMetaTuple(*derived());
}
//! Unmarshall without begin/endStructure, for when composed within another object
void unmarshallFromDbus(const QDBusArgument &arg)
{
baseUnmarshall(static_cast<BaseOfT<Derived> *>(derived()), arg);
using BlackMisc::operator>>;
arg >> Private::EncapsulationBreaker::toMetaTuple(*derived());
}
private:
const Derived *derived() const { return static_cast<const Derived *>(this); }
Derived *derived() { return static_cast<Derived *>(this); }
template <typename T> static void baseMarshall(const T *base, QDBusArgument &arg) { base->marshallToDbus(arg); }
template <typename T> static void baseUnmarshall(T *base, const QDBusArgument &arg) { base->unmarshallFromDbus(arg); }
static void baseMarshall(const void *, QDBusArgument &) {}
static void baseUnmarshall(void *, const QDBusArgument &) {}
};
/*!
* When a derived class and a base class both inherit from Mixin::DBusByTuple,
* the derived class uses this macro to disambiguate the inherited members.
*/
# define BLACKMISC_DECLARE_USING_MIXIN_DBUS(DERIVED) \
using ::BlackMisc::Mixin::DBusByTuple<DERIVED>::marshallToDbus; \
using ::BlackMisc::Mixin::DBusByTuple<DERIVED>::unmarshallFromDbus;
/*!
* CRTP class template which will generate marshalling operators for a derived class with its own marshalling implementation.
*/
template <class Derived>
class JsonOperators
{
public:
//! operator >> for JSON
friend const QJsonObject &operator>>(const QJsonObject &json, Derived &obj)
{
obj.convertFromJson(json);
return json;
}
//! operator >> for JSON
friend const QJsonValue &operator>>(const QJsonValue &json, Derived &obj)
{
obj.convertFromJson(json.toObject());
return json;
}
//! operator >> for JSON
friend const QJsonValueRef &operator>>(const QJsonValueRef &json, Derived &obj)
{
obj.convertFromJson(json.toObject());
return json;
}
//! operator << for JSON
friend QJsonArray &operator<<(QJsonArray &json, const Derived &obj)
{
json.append(obj.toJson());
return json;
}
//! operator << for JSON
friend QJsonObject &operator<<(QJsonObject &json, const std::pair<QString, const Derived &> &value)
{
json.insert(value.first, QJsonValue(value.second.toJson()));
return json;
}
};
/*!
* CRTP class template from which a derived class can inherit common methods dealing with JSON by metatuple.
*/
template <class Derived>
class JsonByTuple : public JsonOperators<Derived>, private Private::EncapsulationBreaker
{
public:
//! Cast to JSON object
QJsonObject toJson() const
{
QJsonObject json = BlackMisc::serializeJson(Private::EncapsulationBreaker::toMetaTuple(*derived()));
return Json::appendJsonObject(json, baseToJson(static_cast<const BaseOfT<Derived> *>(derived())));
}
//! Assign from JSON object
void convertFromJson(const QJsonObject &json)
{
baseConvertFromJson(static_cast<BaseOfT<Derived> *>(derived()), json);
BlackMisc::deserializeJson(json, Private::EncapsulationBreaker::toMetaTuple(*derived()));
}
private:
const Derived *derived() const { return static_cast<const Derived *>(this); }
Derived *derived() { return static_cast<Derived *>(this); }
template <typename T> static QJsonObject baseToJson(const T *base) { return base->toJson(); }
template <typename T> static void baseConvertFromJson(T *base, const QJsonObject &json) { base->convertFromJson(json); }
static QJsonObject baseToJson(const void *) { return {}; }
static void baseConvertFromJson(void *, const QJsonObject &) {}
};
/*!
* When a derived class and a base class both inherit from Mixin::JsonByTuple,
* the derived class uses this macro to disambiguate the inherited members.
*/
# define BLACKMISC_DECLARE_USING_MIXIN_JSON(DERIVED) \
using ::BlackMisc::Mixin::JsonByTuple<DERIVED>::toJson; \
using ::BlackMisc::Mixin::JsonByTuple<DERIVED>::convertFromJson;
/*!
* CRTP class template from which a derived class can inherit operator== implemented using its compare function.
*/
template <class Derived>
class EqualsByCompare
{
public:
//! Equals
friend bool operator ==(const Derived &a, const Derived &b) { return compare(a, b) == 0; }
//! Not equal
friend bool operator !=(const Derived &a, const Derived &b) { return compare(a, b) != 0; }
};
/*!
* CRTP class template from which a derived class can inherit operator== implemented by metatuple.
*/
template <class Derived>
class EqualsByTuple : private Private::EncapsulationBreaker
{
public:
//! Equals
friend bool operator ==(const Derived &a, const Derived &b) { return equals(a, b); }
//! Not equal
friend bool operator !=(const Derived &a, const Derived &b) { return ! equals(a, b); }
private:
static bool equals(const Derived &a, const Derived &b)
{
return toMetaTuple(a) == toMetaTuple(b) && baseEquals(static_cast<const BaseOfT<Derived> *>(&a), static_cast<const BaseOfT<Derived> *>(&b));
}
template <typename T> static bool baseEquals(const T *a, const T *b) { return *a == *b; }
static bool baseEquals(const void *, const void *) { return true; }
};
/*!
* CRTP class template from which a derived class can inherit operator< implemented using its compare function.
*/
template <class Derived>
class LessThanByCompare
{
public:
//! Less than
friend bool operator <(const Derived &a, const Derived &b) { return compare(a, b) < 0; }
//! Greater than
friend bool operator >(const Derived &a, const Derived &b) { return compare(a, b) > 0; }
//! Less than or equal
friend bool operator <=(const Derived &a, const Derived &b) { return compare(a, b) <= 0; }
//! Greater than or equal
friend bool operator >=(const Derived &a, const Derived &b) { return compare(a, b) >= 0; }
};
/*!
* CRTP class template from which a derived class can inherit operator< implemented by metatuple.
*/
template <class Derived>
class LessThanByTuple : private Private::EncapsulationBreaker
{
public:
//! Less than
friend bool operator <(const Derived &a, const Derived &b) { return less(a, b); }
//! Greater than
friend bool operator >(const Derived &a, const Derived &b) { return less(b, a); }
//! Less than or equal
friend bool operator <=(const Derived &a, const Derived &b) { return ! less(b, a); }
//! Greater than or equal
friend bool operator >=(const Derived &a, const Derived &b) { return ! less(a, b); }
private:
static bool less(const Derived &a, const Derived &b)
{
if (baseLess(static_cast<const BaseOfT<Derived> *>(&a), static_cast<const BaseOfT<Derived> *>(&b))) { return true; }
return toMetaTuple(a) < toMetaTuple(b);
}
template <typename T> static bool baseLess(const T *a, const T *b) { return *a < *b; }
static bool baseLess(const void *, const void *) { return false; }
};
/*!
* CRTP class template from which a derived class can inherit non-member compare() implemented by metatuple.
*/
template <class Derived>
class CompareByTuple : private Private::EncapsulationBreaker
{
public:
//! Return negative, zero, or positive if a is less than, equal to, or greater than b.
friend int compare(const Derived &a, const Derived &b) { return compareImpl(a, b); }
private:
static int compareImpl(const Derived &a, const Derived &b)
{
int baseCmp = baseCompare(static_cast<const BaseOfT<Derived> *>(&a), static_cast<const BaseOfT<Derived> *>(&b));
if (baseCmp) { return baseCmp; }
return BlackMisc::compare(toMetaTuple(a), toMetaTuple(b));
}
template <typename T> static int baseCompare(const T *a, const T *b) { return compare(*a, *b); }
static int baseCompare(const void *, const void *) { return 0; }
};
/*!
* CRTP class template from which a derived class can inherit string streaming operations.
*/
template <class Derived>
class String
{
public:
//! Stream << overload to be used in debugging messages
friend QDebug operator<<(QDebug debug, const Derived &obj)
{
debug << obj.stringForStreaming();
return debug;
}
//! Operator << when there is no debug stream
friend QNoDebug operator<<(QNoDebug nodebug, const Derived &obj)
{
Q_UNUSED(obj);
return nodebug;
}
//! Operator << based on text stream
friend QTextStream &operator<<(QTextStream &stream, const Derived &obj)
{
stream << obj.stringForStreaming();
return stream;
}
//! Operator << for QDataStream
friend QDataStream &operator<<(QDataStream &stream, const Derived &obj)
{
stream << obj.stringForStreaming();
return stream;
}
//! Stream operator << for std::cout
friend std::ostream &operator<<(std::ostream &ostr, const Derived &obj)
{
ostr << obj.stringForStreaming().toStdString();
return ostr;
}
//! Cast as QString
QString toQString(bool i18n = false) const { return derived()->convertToQString(i18n); }
//! Cast to pretty-printed QString
QString toFormattedQString(bool i18n = false) const { return derived()->toQString(i18n); }
//! To std string
std::string toStdString(bool i18n = false) const { return derived()->convertToQString(i18n).toStdString(); }
//! String for streaming operators
QString stringForStreaming() const { return derived()->convertToQString(); }
private:
const Derived *derived() const { return static_cast<const Derived *>(this); }
Derived *derived() { return static_cast<Derived *>(this); }
};
/*!
* When a derived class and a base class both inherit from Mixin::String,
* the derived class uses this macro to disambiguate the inherited members.
*/
# define BLACKMISC_DECLARE_USING_MIXIN_STRING(DERIVED) \
using ::BlackMisc::Mixin::String<DERIVED>::toQString; \
using ::BlackMisc::Mixin::String<DERIVED>::toFormattedQString; \
using ::BlackMisc::Mixin::String<DERIVED>::toStdString; \
using ::BlackMisc::Mixin::String<DERIVED>::stringForStreaming;
/*!
* CRTP class template from which a derived class can inherit property indexing functions.
*/
template <class Derived>
class Index
{
public:
//! Base class enums
enum ColumnIndex
{
IndexPixmap = 10, // manually set to avoid circular dependencies
IndexIcon,
IndexString
};
//! Update by variant map
//! \return number of values changed, with skipEqualValues equal values will not be changed
CPropertyIndexList apply(const BlackMisc::CPropertyIndexVariantMap &indexMap, bool skipEqualValues = false); // implemented later due to cyclic include dependency
//! Set property by index
void setPropertyByIndex(const CVariant &variant, const CPropertyIndex &index); // implemented later due to cyclic include dependency
//! Property by index
CVariant propertyByIndex(const BlackMisc::CPropertyIndex &index) const; // implemented later due to cyclic include dependency
//! Property by index as String
QString propertyByIndexAsString(const CPropertyIndex &index, bool i18n = false) const; // implemented later due to cyclic include dependency
//! Is given variant equal to value of property index?
bool equalsPropertyByIndex(const CVariant &compareValue, const CPropertyIndex &index) const; // implemented later due to cyclic include dependency
private:
const Derived *derived() const { return static_cast<const Derived *>(this); }
Derived *derived() { return static_cast<Derived *>(this); }
};
/*!
* CRTP class template from which a derived class can inherit icon-related functions.
*/
template <class Derived, CIcons::IconIndex IconIndex = CIcons::StandardIconUnknown16>
class Icon
{
public:
//! As icon, not implemented by all classes
CIcon toIcon() const; // implemented later due to cyclic include dependency
//! As pixmap, required for most GUI views
QPixmap toPixmap() const; // implemented later due to cyclic include dependency
private:
const Derived *derived() const { return static_cast<const Derived *>(this); }
Derived *derived() { return static_cast<Derived *>(this); }
};
/*!
* When a derived class and a base class both inherit from Mixin::Icon,
* the derived class uses this macro to disambiguate the inherited members.
*/
# define BLACKMISC_DECLARE_USING_MIXIN_ICON(DERIVED) \
using ::BlackMisc::Mixin::Icon<DERIVED>::toIcon; \
using ::BlackMisc::Mixin::Icon<DERIVED>::toPixmap;
}
/*!
* Standard implementation of CValueObject using meta tuple system.
*
@@ -702,108 +174,4 @@ namespace BlackMisc
} // namespace
// TODO Includes due to cyclic dependencies can be removed when CValueObject is split into parts along policy boundaries.
#include "variant.h"
#include "propertyindex.h"
#include "propertyindexlist.h"
#include "iconlist.h"
// TODO Implementations of templates that must appear after those includes, should be moved at the same time that policies are refactored.
namespace BlackMisc
{
namespace Mixin
{
template <class Derived, class... AdditionalTypes>
CVariant MetaType<Derived, AdditionalTypes...>::toCVariant() const
{
return CVariant(derived()->toQVariant());
}
template <class Derived, class... AdditionalTypes>
void MetaType<Derived, AdditionalTypes...>::convertFromCVariant(const CVariant &variant)
{
derived()->convertFromQVariant(variant.getQVariant());
}
template <class Derived>
CPropertyIndexList Index<Derived>::apply(const BlackMisc::CPropertyIndexVariantMap &indexMap, bool skipEqualValues)
{
if (indexMap.isEmpty()) return {};
CPropertyIndexList changed;
const auto &map = indexMap.map();
for (auto it = map.begin(); it != map.end(); ++it)
{
const CVariant value = it.value().toCVariant();
const CPropertyIndex index = it.key();
if (skipEqualValues)
{
bool equal = derived()->equalsPropertyByIndex(value, index);
if (equal) { continue; }
}
derived()->setPropertyByIndex(value, index);
changed.push_back(index);
}
return changed;
}
template <class Derived>
void Index<Derived>::setPropertyByIndex(const CVariant &variant, const CPropertyIndex &index)
{
if (index.isMyself())
{
derived()->convertFromCVariant(variant);
return;
}
// not all classes have implemented nesting
const QString m = QString("Property by index not found (setter), index: ").append(index.toQString());
qFatal("%s", qPrintable(m));
}
template <class Derived>
CVariant Index<Derived>::propertyByIndex(const CPropertyIndex &index) const
{
if (index.isMyself())
{
return derived()->toCVariant();
}
auto i = index.frontCasted<ColumnIndex>();
switch (i)
{
case IndexIcon:
return CVariant::from(derived()->toIcon());
case IndexPixmap:
return CVariant::from(derived()->toPixmap());
case IndexString:
return CVariant(derived()->toQString());
default:
break;
}
// not all classes have implemented nesting
const QString m = QString("Property by index not found, index: ").append(index.toQString());
qFatal("%s", qPrintable(m));
return {};
}
template <class Derived>
QString Index<Derived>::propertyByIndexAsString(const CPropertyIndex &index, bool i18n) const
{
// default implementation, requires propertyByIndex
return derived()->propertyByIndex(index).toQString(i18n);
}
template <class Derived>
bool Index<Derived>::equalsPropertyByIndex(const CVariant &compareValue, const CPropertyIndex &index) const
{
return derived()->propertyByIndex(index) == compareValue;
}
template <class Derived, CIcons::IconIndex IconIndex>
CIcon Icon<Derived, IconIndex>::toIcon() const
{
return CIconList::iconByIndex(IconIndex);
}
template <class Derived, CIcons::IconIndex IconIndex>
QPixmap Icon<Derived, IconIndex>::toPixmap() const
{
return derived()->toIcon().toPixmap();
}
}
}
#endif // guard

View File

@@ -9,12 +9,16 @@
//! \file
#include "valueobject.h" // outside include guard due to cyclic dependency hack (MS)
#ifndef BLACKMISC_VARIANT_H
#define BLACKMISC_VARIANT_H
#include "variant_private.h"
#include "blackmiscexport.h"
#include "blackmiscfreefunctions.h"
#include "tuple.h"
#include "compare.h"
#include "dbus.h"
#include "json.h"
#include <QVariant>
#include <QDateTime>
#include <QJsonValueRef>
@@ -22,12 +26,126 @@
#include <QJsonObject>
#include <QJsonArray>
class QDBusArgument;
namespace BlackMisc
{
class CPropertyIndex;
class CIcon;
namespace Mixin
{
/*!
* CRTP class template from which a derived class can inherit common methods dealing with the metatype of the class.
*/
template <class Derived, class... AdditionalTypes>
class MetaType
{
public:
//! Register metadata
static void registerMetadata()
{
Private::MetaTypeHelper<Derived>::maybeRegisterMetaType();
[](...){}((qRegisterMetaType<AdditionalTypes>(), qDBusRegisterMetaType<AdditionalTypes>(), 0)...);
}
//! Returns the Qt meta type ID of this object.
int getMetaTypeId() const
{
return Private::MetaTypeHelper<Derived>::maybeGetMetaTypeId();
}
//! Returns true if this object is an instance of the class with the given meta type ID, or one of its subclasses.
bool isA(int metaTypeId) const
{
if (metaTypeId == QMetaType::UnknownType) { return false; }
if (metaTypeId == getMetaTypeId()) { return true; }
return baseIsA(static_cast<const MetaBaseOfT<Derived> *>(derived()), metaTypeId);
}
//! Method to return CVariant
//! \deprecated Use CVariant::to() instead.
CVariant toCVariant() const;
//! Set from CVariant
//! \deprecated Use CVariant::from() instead.
void convertFromCVariant(const CVariant &variant);
//! Return QVariant, used with DBus QVariant lists
//! \deprecated Use QVariant::fromValue() instead.
QVariant toQVariant() const
{
return Private::MetaTypeHelper<Derived>::maybeToQVariant(*derived());
}
//! Set from QVariant
//! \deprecated Use QVariant::value() instead.
void convertFromQVariant(const QVariant &variant)
{
return Private::MetaTypeHelper<Derived>::maybeConvertFromQVariant(*derived(), variant);
}
private:
const Derived *derived() const { return static_cast<const Derived *>(this); }
Derived *derived() { return static_cast<Derived *>(this); }
template <typename Base2> static bool baseIsA(const Base2 *base, int metaTypeId) { return base->isA(metaTypeId); }
static bool baseIsA(const void *, int) { return false; }
};
/*!
* Variant of MetaType mixin which also registers QList<Derived> with the type system.
*/
template <class Derived>
class MetaTypeAndQList : public MetaType<Derived, QList<Derived>>
{};
/*!
* When a derived class and a base class both inherit from Mixin::MetaType,
* the derived class uses this macro to disambiguate the inherited members.
*/
# define BLACKMISC_DECLARE_USING_MIXIN_METATYPE(DERIVED) \
using ::BlackMisc::Mixin::MetaType<DERIVED>::registerMetadata; \
using ::BlackMisc::Mixin::MetaType<DERIVED>::getMetaTypeId; \
using ::BlackMisc::Mixin::MetaType<DERIVED>::isA; \
using ::BlackMisc::Mixin::MetaType<DERIVED>::toCVariant; \
using ::BlackMisc::Mixin::MetaType<DERIVED>::toQVariant; \
using ::BlackMisc::Mixin::MetaType<DERIVED>::convertFromCVariant; \
using ::BlackMisc::Mixin::MetaType<DERIVED>::convertFromQVariant;
/*!
* When a derived class and a base class both inherit from Mixin::MetaType,
* the derived class uses this macro to disambiguate the inherited members.
*/
# define BLACKMISC_DECLARE_USING_MIXIN_METATYPE_AND_QLIST(DERIVED) \
using ::BlackMisc::Mixin::MetaTypeAndQList<DERIVED>::registerMetadata; \
using ::BlackMisc::Mixin::MetaTypeAndQList<DERIVED>::getMetaTypeId; \
using ::BlackMisc::Mixin::MetaTypeAndQList<DERIVED>::isA; \
using ::BlackMisc::Mixin::MetaTypeAndQList<DERIVED>::toCVariant; \
using ::BlackMisc::Mixin::MetaTypeAndQList<DERIVED>::toQVariant; \
using ::BlackMisc::Mixin::MetaTypeAndQList<DERIVED>::convertFromCVariant; \
using ::BlackMisc::Mixin::MetaTypeAndQList<DERIVED>::convertFromQVariant;
} // Mixin
/*!
* This registers the value type T with the BlackMisc meta type system,
* making it available for use with the extended feature set of BlackMisc::CVariant.
*
* The implementation (ab)uses the QMetaType converter function registration mechanism
* to store a type-erased representation of the set of operations supported by T.
* Unlike the singleton pattern, this approach means that CVariant can be used in plugins.
*/
template <typename T>
void registerMetaValueType()
{
if (QMetaType::hasRegisteredConverterFunction<T, Private::IValueObjectMetaInfo *>()) { return; }
auto converter = [](const T &) { static Private::CValueObjectMetaInfo<T> info; return &info; };
bool ok = QMetaType::registerConverter<T, Private::IValueObjectMetaInfo *>(converter);
Q_ASSERT(ok);
Q_UNUSED(ok);
}
/*!
* Wrapper around QVariant which provides transparent access to CValueObject methods
* of the contained object if it is registered with BlackMisc::registerMetaValueType.
@@ -38,9 +156,7 @@ namespace BlackMisc
public Mixin::LessThanByCompare<CVariant>,
public Mixin::DBusOperators<CVariant>,
public Mixin::JsonOperators<CVariant>,
public Mixin::Index<CVariant>,
public Mixin::String<CVariant>,
public Mixin::Icon<CVariant>
public Mixin::String<CVariant>
{
public:
//! Default constructor.
@@ -252,6 +368,21 @@ namespace BlackMisc
//! \private Needed so we can copy forward-declared CVariant.
inline void assign(CVariant &a, const CVariant &b) { a = b; }
}
namespace Mixin
{
template <class Derived, class... AdditionalTypes>
CVariant MetaType<Derived, AdditionalTypes...>::toCVariant() const
{
return CVariant(derived()->toQVariant());
}
template <class Derived, class... AdditionalTypes>
void MetaType<Derived, AdditionalTypes...>::convertFromCVariant(const CVariant &variant)
{
derived()->convertFromQVariant(variant.getQVariant());
}
}
} // namespace
Q_DECLARE_METATYPE(BlackMisc::CVariant)

View File

@@ -9,10 +9,11 @@
//! \file
#ifndef BLACKMISC_VALUEOBJECT_PRIVATE_H
#define BLACKMISC_VALUEOBJECT_PRIVATE_H
#ifndef BLACKMISC_VARIANT_PRIVATE_H
#define BLACKMISC_VARIANT_PRIVATE_H
#include "blackmisc/blackmiscexport.h"
#include "blackmisc/blackmiscfreefunctions.h"
#include "blackmisc/inheritance_traits.h"
#include <QString>
#include <QMetaType>