mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-04-05 01:05:34 +08:00
refs #356 Removed CValueObject and expanded CValueObjectStdTuple to compensate.
* Involves rearranging some header includes to break cyclic include dependencies, * Adding a new, simple base class CEmpty, * Removing any remaining polymorphic uses of CValueObject with templates, * Adding a new trait for use with enable_if to restrict templates to work only with value objects, * Replacing the polymorphic/runtime multimethod-based compare functions with static/compile-time compare functions.
This commit is contained in:
@@ -9,6 +9,8 @@
|
||||
|
||||
//! \file
|
||||
|
||||
#include "valueobject.h" // outside include guard due to cyclic dependency hack (MS)
|
||||
|
||||
#ifndef BLACKMISC_COLLECTION_H
|
||||
#define BLACKMISC_COLLECTION_H
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#ifndef BLACKMISC_CONTAINERBASE_H
|
||||
#define BLACKMISC_CONTAINERBASE_H
|
||||
|
||||
#include "valueobject.h"
|
||||
#include "range.h"
|
||||
#include "propertyindexvariantmap.h"
|
||||
#include "blackmiscfreefunctions.h"
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
|
||||
#include "icon.h"
|
||||
#include "pqangle.h"
|
||||
|
||||
namespace BlackMisc
|
||||
{
|
||||
|
||||
@@ -12,12 +12,17 @@
|
||||
#ifndef BLACKMISC_ICON_H
|
||||
#define BLACKMISC_ICON_H
|
||||
|
||||
#include "valueobject.h"
|
||||
#include "icons.h"
|
||||
#include "pqangle.h"
|
||||
#include <QIcon>
|
||||
|
||||
namespace BlackMisc
|
||||
{
|
||||
namespace PhysicalQuantities
|
||||
{
|
||||
class CAngle;
|
||||
}
|
||||
|
||||
//! 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 CIcon : public CValueObjectStdTuple<CIcon>
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#ifndef BLACKMISC_ICONLIST_H
|
||||
#define BLACKMISC_ICONLIST_H
|
||||
|
||||
#include "valueobject.h"
|
||||
#include "sequence.h"
|
||||
#include "collection.h"
|
||||
#include "icon.h"
|
||||
|
||||
@@ -137,7 +137,8 @@ namespace BlackMisc
|
||||
CLogMessage &operator <<(QChar v) { return arg(v); }
|
||||
CLogMessage &operator <<(char v) { return arg(QChar(v)); }
|
||||
CLogMessage &operator <<(double v) { return arg(QString::number(v)); }
|
||||
CLogMessage &operator <<(const CValueObject &v) { return arg(v.toQString()); }
|
||||
template <class T, class = typename std::enable_if<IsValueObject<T>::value>::type>
|
||||
CLogMessage &operator <<(const T &v) { return arg(v.toQString()); }
|
||||
//! @}
|
||||
|
||||
//! Sends a verbatim, preformatted message to the log.
|
||||
|
||||
@@ -9,10 +9,11 @@
|
||||
|
||||
//! \file
|
||||
|
||||
#include "valueobject.h" // outside include guard due to cyclic dependency hack (MS)
|
||||
|
||||
#ifndef BLACKMISC_PROPERTYINDEX_H
|
||||
#define BLACKMISC_PROPERTYINDEX_H
|
||||
|
||||
#include "valueobject.h"
|
||||
#include "blackmiscfreefunctions.h"
|
||||
#include <initializer_list>
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
//! \file
|
||||
|
||||
#include "valueobject.h" // outside include guard due to cyclic dependency hack (MS)
|
||||
|
||||
#ifndef BLACKMISC_PROPERTYINDEXLIST_H
|
||||
#define BLACKMISC_PROPERTYINDEXLIST_H
|
||||
|
||||
|
||||
@@ -84,38 +84,6 @@ namespace BlackMisc
|
||||
return !(valueMap == variant);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare with CValueObject
|
||||
*/
|
||||
bool operator==(const CPropertyIndexVariantMap &indexMap, const CValueObject &valueObject)
|
||||
{
|
||||
return indexMap == valueObject.toCVariant();
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare with CValueObject
|
||||
*/
|
||||
bool operator!=(const CPropertyIndexVariantMap &indexMap, const CValueObject &valueObject)
|
||||
{
|
||||
return !(indexMap == valueObject);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare with CValueObject
|
||||
*/
|
||||
bool operator==(const CValueObject &valueObject, const CPropertyIndexVariantMap &valueMap)
|
||||
{
|
||||
return valueMap == valueObject;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare with CValueObject
|
||||
*/
|
||||
bool operator!=(const CValueObject &valueObject, const CPropertyIndexVariantMap &valueMap)
|
||||
{
|
||||
return !(valueMap == valueObject);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert to string
|
||||
*/
|
||||
|
||||
@@ -111,19 +111,23 @@ namespace BlackMisc
|
||||
|
||||
//! Operator == with CValueObject
|
||||
//! \todo Still needed?
|
||||
friend bool operator ==(const CPropertyIndexVariantMap &valueMap, const CValueObject &valueObject);
|
||||
template <class T, class = typename std::enable_if<IsValueObject<T>::value>::type>
|
||||
friend bool operator ==(const CPropertyIndexVariantMap &valueMap, const T &valueObject) { return valueMap == CVariant::from(valueObject); }
|
||||
|
||||
//! Operator != with CValueObject
|
||||
//! \todo Still needed?
|
||||
friend bool operator !=(const CPropertyIndexVariantMap &valueMap, const CValueObject &valueObject);
|
||||
template <class T, class = typename std::enable_if<IsValueObject<T>::value>::type>
|
||||
friend bool operator !=(const CPropertyIndexVariantMap &valueMap, const T &valueObject) { return valueMap != CVariant::from(valueObject); }
|
||||
|
||||
//! Operator == with CValueObject
|
||||
//! \todo Still needed?
|
||||
friend bool operator ==(const CValueObject &valueObject, const CPropertyIndexVariantMap &valueMap);
|
||||
template <class T, class = typename std::enable_if<IsValueObject<T>::value>::type>
|
||||
friend bool operator ==(const T &valueObject, const CPropertyIndexVariantMap &valueMap) { return valueMap == CVariant::from(valueObject); }
|
||||
|
||||
//! Operator != with CValueObject
|
||||
//! \todo Still needed?
|
||||
friend bool operator !=(const CValueObject &valueObject, const CPropertyIndexVariantMap &valueMap);
|
||||
template <class T, class = typename std::enable_if<IsValueObject<T>::value>::type>
|
||||
friend bool operator !=(const T &valueObject, const CPropertyIndexVariantMap &valueMap) { return valueMap != CVariant::from(valueObject); }
|
||||
|
||||
//! Map
|
||||
const QMap<CPropertyIndex, CVariant> &map() const { return this->m_values; }
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
//! \file
|
||||
|
||||
#include "valueobject.h" // outside include guard due to cyclic dependency hack (MS)
|
||||
|
||||
#ifndef BLACKMISC_SEQUENCE_H
|
||||
#define BLACKMISC_SEQUENCE_H
|
||||
|
||||
|
||||
@@ -25,8 +25,26 @@
|
||||
|
||||
namespace BlackMisc
|
||||
{
|
||||
class CValueObject;
|
||||
class CEmpty;
|
||||
template <class> class TupleConverter;
|
||||
template <class Derived, class Base = CEmpty> class CValueObjectStdTuple;
|
||||
|
||||
//! Traits class to test whether a class template specialization is a base class of another class.
|
||||
template <template <class...> class Base, class Derived>
|
||||
class TemplateIsBaseOf
|
||||
{
|
||||
struct yes { char x; };
|
||||
struct no { yes x[2]; };
|
||||
template <class... Ts> static yes test(const Base<Ts...> &);
|
||||
static no test(...);
|
||||
|
||||
public:
|
||||
//! True if and only if Derived is derived from a specialization of Base.
|
||||
static const bool value = sizeof(test(std::declval<Derived>())) == sizeof(yes);
|
||||
|
||||
//! std::true_type or std::false_type.
|
||||
using type = std::integral_constant<bool, value>;
|
||||
};
|
||||
|
||||
namespace Private
|
||||
{
|
||||
@@ -145,7 +163,7 @@ namespace BlackMisc
|
||||
template <class Derived>
|
||||
struct AttributeComparable<Derived, false, false>
|
||||
{
|
||||
template <class T> using isCValueObject = typename std::is_base_of<CValueObject, typename T::type>::type;
|
||||
template <class T> using isCValueObject = typename std::is_base_of<CEmpty, T>::type; // FIXME use TemplateIsBaseOf
|
||||
friend int compare(const Derived &a, const Derived &b) { return compareHelper(a.m_obj, b.m_obj, isCValueObject<Derived>()); }
|
||||
friend bool operator ==(const Derived &a, const Derived &b) { return a.m_obj == b.m_obj; }
|
||||
friend bool operator !=(const Derived &a, const Derived &b) { return a.m_obj != b.m_obj; }
|
||||
|
||||
@@ -7,211 +7,18 @@
|
||||
* contained in the LICENSE file.
|
||||
*/
|
||||
|
||||
|
||||
#include "valueobject.h"
|
||||
#include "propertyindexvariantmap.h"
|
||||
#include "propertyindexlist.h"
|
||||
#include "propertyindex.h"
|
||||
#include "iconlist.h"
|
||||
#include "blackmiscfreefunctions.h"
|
||||
|
||||
namespace BlackMisc
|
||||
{
|
||||
|
||||
/*
|
||||
* Stringify
|
||||
*/
|
||||
QString CValueObject::toQString(bool i18n) const
|
||||
{
|
||||
return this->convertToQString(i18n);
|
||||
}
|
||||
|
||||
/*
|
||||
* Stringify
|
||||
*/
|
||||
QString CValueObject::toFormattedQString(bool i18n) const
|
||||
{
|
||||
return this->toQString(i18n);
|
||||
}
|
||||
|
||||
/*
|
||||
* Stringify
|
||||
*/
|
||||
std::string CValueObject::toStdString(bool i18n) const
|
||||
{
|
||||
return this->convertToQString(i18n).toStdString();
|
||||
}
|
||||
|
||||
/*
|
||||
* Streaming
|
||||
*/
|
||||
QString CValueObject::stringForStreaming() const
|
||||
{
|
||||
// simplest default implementation requires only one method
|
||||
return this->convertToQString();
|
||||
}
|
||||
|
||||
/*
|
||||
* Setter for property by index
|
||||
*/
|
||||
void CValueObject::setPropertyByIndex(const CVariant &variant, const CPropertyIndex &index)
|
||||
{
|
||||
if (index.isMyself())
|
||||
{
|
||||
this->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));
|
||||
}
|
||||
|
||||
/*
|
||||
* By index
|
||||
*/
|
||||
CVariant CValueObject::propertyByIndex(const BlackMisc::CPropertyIndex &index) const
|
||||
{
|
||||
if (index.isMyself()) { return this->toCVariant(); }
|
||||
ColumnIndex i = index.frontCasted<ColumnIndex>();
|
||||
switch (i)
|
||||
{
|
||||
case IndexIcon:
|
||||
return this->toIcon().toCVariant();
|
||||
case IndexPixmap:
|
||||
return CVariant::from(this->toPixmap());
|
||||
case IndexString:
|
||||
return CVariant(this->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 CVariant(m); // avoid compiler warning
|
||||
}
|
||||
|
||||
/*
|
||||
* By index as string
|
||||
*/
|
||||
QString CValueObject::propertyByIndexAsString(const CPropertyIndex &index, bool i18n) const
|
||||
{
|
||||
// default implementation, requires propertyByIndex
|
||||
return this->propertyByIndex(index).toQString(i18n);
|
||||
}
|
||||
|
||||
/*
|
||||
* Variant equal property index?
|
||||
*/
|
||||
bool CValueObject::equalsPropertyByIndex(const CVariant &compareValue, const CPropertyIndex &index) const
|
||||
{
|
||||
return this->propertyByIndex(index) == compareValue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare
|
||||
*/
|
||||
int compare(const CValueObject &v1, const CValueObject &v2)
|
||||
{
|
||||
if (v1.isA(v2.getMetaTypeId()))
|
||||
{
|
||||
return v2.compareImpl(v1) * -1;
|
||||
}
|
||||
else if (v2.isA(v1.getMetaTypeId()))
|
||||
{
|
||||
return v1.compareImpl(v2);
|
||||
}
|
||||
else
|
||||
{
|
||||
Q_ASSERT_X(false, Q_FUNC_INFO, "Attempt to compare between instances of unrelated classes");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* apply, return changed indexes
|
||||
*/
|
||||
CPropertyIndexList CValueObject::apply(const BlackMisc::CPropertyIndexVariantMap &indexMap, bool skipEqualValues)
|
||||
{
|
||||
CPropertyIndexList changed;
|
||||
if (indexMap.isEmpty()) return 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 = this->equalsPropertyByIndex(value, index);
|
||||
if (equal) { continue; }
|
||||
}
|
||||
this->setPropertyByIndex(value, index);
|
||||
changed.push_back(index);
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
/*
|
||||
* Icon
|
||||
*/
|
||||
CIcon CValueObject::toIcon() const
|
||||
CIcon CEmpty::toIcon() const
|
||||
{
|
||||
return CIconList::iconByIndex(CIcons::StandardIconUnknown16);
|
||||
}
|
||||
|
||||
/*
|
||||
* Pixmap
|
||||
*/
|
||||
QPixmap CValueObject::toPixmap() const
|
||||
QPixmap CEmpty::toPixmap() const
|
||||
{
|
||||
return this->toIcon().toPixmap();
|
||||
}
|
||||
|
||||
/*
|
||||
* from DBus
|
||||
*/
|
||||
const QDBusArgument &operator>>(const QDBusArgument &argument, CValueObject &valueObject)
|
||||
{
|
||||
argument.beginStructure();
|
||||
valueObject.unmarshallFromDbus(argument);
|
||||
argument.endStructure();
|
||||
return argument;
|
||||
}
|
||||
|
||||
/*
|
||||
* to DBus
|
||||
*/
|
||||
QDBusArgument &operator<<(QDBusArgument &argument, const CValueObject &valueObject)
|
||||
{
|
||||
argument.beginStructure();
|
||||
valueObject.marshallToDbus(argument);
|
||||
argument.endStructure();
|
||||
return argument;
|
||||
}
|
||||
|
||||
/*
|
||||
* to CVariant
|
||||
*/
|
||||
CVariant CValueObject::toCVariant() const
|
||||
{
|
||||
return CVariant(this->toQVariant());
|
||||
}
|
||||
|
||||
/*
|
||||
* from CVariant
|
||||
*/
|
||||
void CValueObject::convertFromCVariant(const CVariant &variant)
|
||||
{
|
||||
this->convertFromQVariant(variant.getQVariant());
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementations of pure virtual functions
|
||||
*/
|
||||
uint CValueObject::getValueHash() const { return 0; }
|
||||
int CValueObject::compareImpl(const CValueObject &) const { return 0; }
|
||||
void CValueObject::marshallToDbus(QDBusArgument &) const {}
|
||||
void CValueObject::unmarshallFromDbus(const QDBusArgument &) {}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,12 @@ namespace BlackMisc
|
||||
class CPropertyIndexVariantMap;
|
||||
class CIcon;
|
||||
class CVariant;
|
||||
class CEmpty;
|
||||
|
||||
//! Traits class to test whether a class is derived from CValueObjectStdTuple.
|
||||
//! \todo TemplateIsBaseOf gives incorrect result due to ambiguity if there is more than one specialization of CValueObjectStdTuple which is a base of T.
|
||||
template <class T>
|
||||
using IsValueObject = typename std::is_base_of<CEmpty, T>::type;
|
||||
|
||||
/*!
|
||||
* This registers the value type T with the BlackMisc meta type system,
|
||||
@@ -57,189 +63,76 @@ namespace BlackMisc
|
||||
}
|
||||
|
||||
/*!
|
||||
* Base class for value types.
|
||||
* Default base class for CValueObjectStdTuple.
|
||||
*/
|
||||
class CValueObject
|
||||
class CEmpty
|
||||
{
|
||||
//! Stream << overload to be used in debugging messages
|
||||
friend QDebug operator<<(QDebug debug, const CValueObject &uc)
|
||||
{
|
||||
debug << uc.stringForStreaming();
|
||||
return debug;
|
||||
}
|
||||
|
||||
//! Operator << based on text stream
|
||||
friend QTextStream &operator<<(QTextStream &textStream, const CValueObject &uc)
|
||||
{
|
||||
textStream << uc.stringForStreaming();
|
||||
return textStream;
|
||||
}
|
||||
|
||||
//! Operator << when there is no debug stream
|
||||
friend QNoDebug operator<<(QNoDebug nodebug, const CValueObject &valueObject)
|
||||
{
|
||||
Q_UNUSED(valueObject);
|
||||
return nodebug;
|
||||
}
|
||||
|
||||
//! Operator << for QDataStream
|
||||
friend QDataStream &operator<<(QDataStream &stream, const CValueObject &valueObject)
|
||||
{
|
||||
stream << valueObject.stringForStreaming();
|
||||
return stream;
|
||||
}
|
||||
|
||||
//! Stream operator << for std::cout
|
||||
friend std::ostream &operator<<(std::ostream &ostr, const CValueObject &uc)
|
||||
{
|
||||
ostr << uc.stringForStreaming().toStdString();
|
||||
return ostr;
|
||||
}
|
||||
|
||||
//! Unmarshalling operator >>, DBus to object
|
||||
friend const QDBusArgument &operator>>(const QDBusArgument &argument, CValueObject &valueObject);
|
||||
|
||||
//! Marshalling operator <<, object to DBus
|
||||
friend QDBusArgument &operator<<(QDBusArgument &argument, const CValueObject &valueObject);
|
||||
|
||||
/*!
|
||||
* Compares two instances of related classes
|
||||
* and returns an integer less than, equal to, or greater than zero
|
||||
* if v1 is less than, equal to, or greater than v2.
|
||||
* \pre The runtime types of the two objects must be the same or related by inheritance.
|
||||
*/
|
||||
friend int compare(const CValueObject &v1, const CValueObject &v2);
|
||||
|
||||
public:
|
||||
//! Root
|
||||
using base_type = CValueObject;
|
||||
//! Base class is alias of itself
|
||||
using base_type = CEmpty;
|
||||
|
||||
//! Base class enums
|
||||
enum ColumnIndex
|
||||
{
|
||||
IndexPixmap = 10, // manually set to avoid circular dependencies
|
||||
IndexIcon,
|
||||
IndexString
|
||||
};
|
||||
|
||||
//! Virtual destructor
|
||||
virtual ~CValueObject() {}
|
||||
|
||||
//! Cast as QString
|
||||
QString toQString(bool i18n = false) const;
|
||||
|
||||
//! Cast to pretty-printed QString
|
||||
virtual QString toFormattedQString(bool i18n = false) const;
|
||||
|
||||
//! To std string
|
||||
std::string toStdString(bool i18n = false) const;
|
||||
|
||||
//! 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);
|
||||
|
||||
//! Value hash, allows comparisons between QVariants
|
||||
virtual uint getValueHash() const = 0;
|
||||
|
||||
//! Virtual method to return QVariant, used with DBus QVariant lists
|
||||
virtual QVariant toQVariant() const = 0;
|
||||
|
||||
//! Method to return CVariant
|
||||
CVariant toCVariant() const;
|
||||
|
||||
//! Set from QVariant
|
||||
virtual void convertFromQVariant(const QVariant &variant) = 0;
|
||||
|
||||
//! Set from CVariant
|
||||
void convertFromCVariant(const CVariant &variant);
|
||||
|
||||
//! Contribute to JSON object
|
||||
virtual QJsonObject toJson() const { QJsonObject json; return json;}
|
||||
|
||||
//! Initialize from JSON object
|
||||
virtual void convertFromJson(const QJsonObject &json) { Q_UNUSED(json); }
|
||||
|
||||
//! As icon, not implement by all classes
|
||||
//! As icon, not implemented by all classes
|
||||
//! \todo Here because incomplete type. Move to policy class during policy refactoring.
|
||||
virtual CIcon toIcon() const;
|
||||
|
||||
//! As pixmap, required for most GUI views
|
||||
//! \todo Here because incomplete type. Move to policy class during policy refactoring.
|
||||
virtual QPixmap toPixmap() const;
|
||||
|
||||
//! Set property by index
|
||||
virtual void setPropertyByIndex(const CVariant &variant, const CPropertyIndex &index);
|
||||
|
||||
//! Property by index
|
||||
virtual CVariant propertyByIndex(const BlackMisc::CPropertyIndex &index) const;
|
||||
|
||||
//! Property by index as String
|
||||
//! \details Intentionally not abstract, avoiding all classes need to implement this method
|
||||
virtual QString propertyByIndexAsString(const CPropertyIndex &index, bool i18n = false) const;
|
||||
|
||||
//! Is given variant equal to value of property index?
|
||||
virtual bool equalsPropertyByIndex(const CVariant &compareValue, const CPropertyIndex &index) const;
|
||||
|
||||
//! Parse from string, e.g. 100km/h
|
||||
//! \todo Here to avoid name hiding in PQ classes. Fix during policy refactoring.
|
||||
virtual void parseFromString(const QString &) { qFatal("Not implemented"); }
|
||||
|
||||
protected:
|
||||
template <typename T>
|
||||
friend struct Private::CValueObjectMetaInfo;
|
||||
|
||||
//! Default constructor.
|
||||
CValueObject() = default;
|
||||
|
||||
//! Copy constructor.
|
||||
CValueObject(const CValueObject &) = default;
|
||||
|
||||
//! Copy assignment operator.
|
||||
CValueObject &operator =(const CValueObject &) = default;
|
||||
|
||||
//! String for streaming operators
|
||||
virtual QString stringForStreaming() const;
|
||||
|
||||
//! String for QString conversion
|
||||
//! \todo Here because pure virtual. Move to CValueObjectStdTuple when all dynamic polymorphism is removed.
|
||||
virtual QString convertToQString(bool i18n = false) const = 0;
|
||||
|
||||
//! Returns the Qt meta type ID of this object.
|
||||
virtual int getMetaTypeId() const = 0;
|
||||
//! Protected default constructor
|
||||
CEmpty() = default;
|
||||
|
||||
/*!
|
||||
* Returns true if this object is an instance of the class with the given meta type ID,
|
||||
* or one of its subclasses.
|
||||
*/
|
||||
virtual bool isA(int metaTypeId) const { Q_UNUSED(metaTypeId); return false; }
|
||||
//! Protected copy constructor
|
||||
CEmpty(const CEmpty &) = default;
|
||||
|
||||
/*!
|
||||
* Compare this value with another value of the same type
|
||||
* \param other
|
||||
* \return Less than, equal to, or greater than zero if this is
|
||||
* less than, equal to, or greather than other.
|
||||
* \pre Other must have the same runtime type as the this object.
|
||||
* \remark It is usually safer to use the friend function compare() instead.
|
||||
*/
|
||||
virtual int compareImpl(const CValueObject &other) const = 0;
|
||||
//! Protected copy assignment operator
|
||||
CEmpty &operator =(const CEmpty &) = default;
|
||||
|
||||
//! Marshall to DBus
|
||||
virtual void marshallToDbus(QDBusArgument &) const = 0;
|
||||
|
||||
//! Unmarshall from DBus
|
||||
virtual void unmarshallFromDbus(const QDBusArgument &) = 0;
|
||||
//! Non-virtual protected destructor
|
||||
~CEmpty() = default;
|
||||
};
|
||||
|
||||
//! \private FIXME defined out-of-line because it depends on CValueObject
|
||||
template <typename T>
|
||||
int Private::CValueObjectMetaInfo<T>::compare(const void *lhs, const void *rhs) const
|
||||
//! Dummy comparison.
|
||||
inline int compare(const CEmpty &, const CEmpty &) { return 0; }
|
||||
|
||||
/*!
|
||||
* Terminating base cases for the recursive methods of CValueObjectStdTuple.
|
||||
*/
|
||||
struct CValueObjectDummyBase
|
||||
{
|
||||
// Casting to CValueObject here only because compareImpl is protected and CValueObjectMetaInfo is a friend of CValueObject.
|
||||
// TODO: make compareImpl public (and typesafe), then this doesn't need to be defined out-of-line.
|
||||
return static_cast<const CValueObject *>(lhs)->compareImpl(*static_cast<const CValueObject *>(rhs));
|
||||
}
|
||||
//! Value hash
|
||||
static uint getValueHash() { return 0; }
|
||||
|
||||
//! To JSON
|
||||
static QJsonObject toJson() { return {}; }
|
||||
|
||||
//! From JSON
|
||||
static void convertFromJson(const QJsonObject &) {}
|
||||
|
||||
//! Is a
|
||||
static bool isA(int) { return false; }
|
||||
|
||||
//! Marshall to DBus
|
||||
static void marshallToDbus(QDBusArgument &) {}
|
||||
|
||||
//! Unmarshall from DBus
|
||||
static void unmarshallFromDbus(const QDBusArgument &) {}
|
||||
};
|
||||
|
||||
/*!
|
||||
* Default policy classes for use by CValueObjectStdTuple.
|
||||
*
|
||||
* The default policies are inherited from the policies of the base class. There is a specialization
|
||||
* for the terminating case in which the base class is CValueObject.
|
||||
* for the terminating case in which the base class is CEmpty.
|
||||
*
|
||||
* Specialize this template to use non-default policies for a particular derived class.
|
||||
* Due to the void default template parameter, specializations can inherit from CValueObjectStdTuplePolicy<>
|
||||
@@ -261,9 +154,9 @@ namespace BlackMisc
|
||||
/*!
|
||||
* Default policy classes for use by CValueObjectStdTuple.
|
||||
*
|
||||
* Specialization for the terminating case in which the base class is CValueObject.
|
||||
* Specialization for the terminating case in which the base class is CEmpty.
|
||||
*/
|
||||
template <> struct CValueObjectStdTuplePolicy<CValueObject>
|
||||
template <> struct CValueObjectStdTuplePolicy<CEmpty>
|
||||
{
|
||||
using MetaType = Policy::MetaType::Default; //!< Metatype policy
|
||||
using Equals = Policy::Equals::MetaTuple; //!< Equals policy
|
||||
@@ -282,64 +175,181 @@ namespace BlackMisc
|
||||
* to specify different policy classes.
|
||||
*
|
||||
* \tparam Derived The class which is inheriting from this one (CRTP).
|
||||
* \tparam Base The class which this one shall inherit from (default is CValueObject,
|
||||
* \tparam Base The class which this one shall inherit from (default is CEmpty,
|
||||
* but this can be changed to create a deeper inheritance hierarchy).
|
||||
*/
|
||||
template <class Derived, class Base = CValueObject> class CValueObjectStdTuple :
|
||||
template <class Derived, class Base /*= CEmpty*/> class CValueObjectStdTuple :
|
||||
public Base,
|
||||
private CValueObjectStdTuplePolicy<Derived>::Equals::template Ops<Derived, Base>,
|
||||
private CValueObjectStdTuplePolicy<Derived>::LessThan::template Ops<Derived, Base>
|
||||
private CValueObjectStdTuplePolicy<Derived>::LessThan::template Ops<Derived, Base>,
|
||||
private CValueObjectStdTuplePolicy<Derived>::Compare::template Ops<Derived, Base>
|
||||
{
|
||||
static_assert(std::is_base_of<CValueObject, Base>::value, "Base must be derived from CValueObject");
|
||||
static_assert(std::is_same<CEmpty, Base>::value || IsValueObject<Base>::value, "Base must be either CEmpty or derived from CValueObjectStdTuple");
|
||||
|
||||
using MetaTypePolicy = typename CValueObjectStdTuplePolicy<Derived>::MetaType;
|
||||
using ComparePolicy = typename CValueObjectStdTuplePolicy<Derived>::Compare;
|
||||
using HashPolicy = typename CValueObjectStdTuplePolicy<Derived>::Hash;
|
||||
using DBusPolicy = typename CValueObjectStdTuplePolicy<Derived>::DBus;
|
||||
using JsonPolicy = typename CValueObjectStdTuplePolicy<Derived>::Json;
|
||||
using PropertyIndexPolicy = typename CValueObjectStdTuplePolicy<Derived>::PropertyIndex;
|
||||
|
||||
using BaseOrDummy = typename std::conditional<std::is_same<Base, CEmpty>::value, CValueObjectDummyBase, Base>::type;
|
||||
|
||||
//! 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 &textStream, const Derived &obj)
|
||||
{
|
||||
textStream << obj.stringForStreaming();
|
||||
return textStream;
|
||||
}
|
||||
|
||||
//! Operator << for QDataStream
|
||||
friend QDataStream &operator<<(QDataStream &stream, const Derived &valueObject)
|
||||
{
|
||||
stream << valueObject.stringForStreaming();
|
||||
return stream;
|
||||
}
|
||||
|
||||
//! Stream operator << for std::cout
|
||||
friend std::ostream &operator<<(std::ostream &ostr, const Derived &obj)
|
||||
{
|
||||
ostr << obj.stringForStreaming().toStdString();
|
||||
return ostr;
|
||||
}
|
||||
|
||||
//! Unmarshalling operator >>, DBus to object
|
||||
friend const QDBusArgument &operator>>(const QDBusArgument &arg, Derived &obj)
|
||||
{
|
||||
arg.beginStructure();
|
||||
static_cast<CValueObjectStdTuple &>(obj).unmarshallFromDbus(arg); // virtual method is protected in Derived
|
||||
arg.endStructure();
|
||||
return arg;
|
||||
}
|
||||
|
||||
//! Marshalling operator <<, object to DBus
|
||||
friend QDBusArgument &operator<<(QDBusArgument &arg, const Derived &obj)
|
||||
{
|
||||
arg.beginStructure();
|
||||
static_cast<const CValueObjectStdTuple &>(obj).marshallToDbus(arg); // virtual method is protected in Derived
|
||||
arg.endStructure();
|
||||
return arg;
|
||||
}
|
||||
|
||||
//! operator >> for JSON
|
||||
friend const QJsonObject &operator>>(const QJsonObject &json, Derived &valueObject)
|
||||
{
|
||||
valueObject.convertFromJson(json);
|
||||
return json;
|
||||
}
|
||||
|
||||
//! operator >> for JSON
|
||||
friend const QJsonValue &operator>>(const QJsonValue &json, Derived &valueObject)
|
||||
{
|
||||
valueObject.convertFromJson(json.toObject());
|
||||
return json;
|
||||
}
|
||||
|
||||
//! operator >> for JSON
|
||||
friend const QJsonValueRef &operator>>(const QJsonValueRef &json, Derived &valueObject)
|
||||
{
|
||||
valueObject.convertFromJson(json.toObject());
|
||||
return json;
|
||||
}
|
||||
|
||||
//! operator << for JSON
|
||||
friend QJsonArray &operator<<(QJsonArray &json, const Derived &value)
|
||||
{
|
||||
json.append(value.toJson());
|
||||
return json;
|
||||
}
|
||||
|
||||
//! operator << for JSON
|
||||
friend QJsonObject& operator<<(QJsonObject &json, const std::pair<QString, Derived> &value)
|
||||
{
|
||||
json.insert(value.first, QJsonValue(value.second.toJson()));
|
||||
return json;
|
||||
}
|
||||
|
||||
//! qHash overload, needed for storing value in a QSet.
|
||||
friend uint qHash(const Derived &value, uint seed = 0)
|
||||
{
|
||||
return qHash(value.getValueHash(), seed);
|
||||
}
|
||||
|
||||
public:
|
||||
//! Base class
|
||||
using base_type = Base;
|
||||
|
||||
//! Destructor
|
||||
virtual ~CValueObjectStdTuple() {}
|
||||
|
||||
//! Base class enums
|
||||
enum ColumnIndex
|
||||
{
|
||||
IndexPixmap = 10, // manually set to avoid circular dependencies
|
||||
IndexIcon,
|
||||
IndexString
|
||||
};
|
||||
|
||||
//! Cast as QString
|
||||
QString toQString(bool i18n = false) const { return this->convertToQString(i18n); }
|
||||
|
||||
//! Cast to pretty-printed QString
|
||||
virtual QString toFormattedQString(bool i18n = false) const { return this->toQString(i18n); }
|
||||
|
||||
//! To std string
|
||||
std::string toStdString(bool i18n = false) const { return this->convertToQString(i18n).toStdString(); }
|
||||
|
||||
//! 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)
|
||||
CPropertyIndexList apply(const BlackMisc::CPropertyIndexVariantMap &indexMap, bool skipEqualValues = false); // implemented later due to cyclic include dependency
|
||||
|
||||
//! Method to return CVariant
|
||||
CVariant toCVariant() const;
|
||||
|
||||
//! Set from CVariant
|
||||
void convertFromCVariant(const CVariant &variant);
|
||||
|
||||
//! Value hash, allows comparisons between QVariants
|
||||
virtual uint getValueHash() const
|
||||
{
|
||||
CPropertyIndexList result;
|
||||
PropertyIndexPolicy::apply(*derived(), indexMap, result, skipEqualValues);
|
||||
return result;
|
||||
return HashPolicy::hashImpl(*derived()) ^ BaseOrDummy::getValueHash();
|
||||
}
|
||||
|
||||
//! \copydoc CValueObject::getValueHash()
|
||||
virtual uint getValueHash() const override
|
||||
{
|
||||
return HashPolicy::hashImpl(*derived()) ^ Base::getValueHash();
|
||||
}
|
||||
|
||||
//! \copydoc CValueObject::toJson
|
||||
virtual QJsonObject toJson() const override
|
||||
//! Cast to JSON object
|
||||
virtual QJsonObject toJson() const
|
||||
{
|
||||
QJsonObject json = JsonPolicy::serializeImpl(*derived());
|
||||
return Json::appendJsonObject(json, Base::toJson());
|
||||
return Json::appendJsonObject(json, BaseOrDummy::toJson());
|
||||
}
|
||||
|
||||
//! \copydoc CValueObject::convertFromJson
|
||||
virtual void convertFromJson(const QJsonObject &json) override
|
||||
//! Assign from JSON object
|
||||
virtual void convertFromJson(const QJsonObject &json)
|
||||
{
|
||||
Base::convertFromJson(json);
|
||||
BaseOrDummy::convertFromJson(json);
|
||||
JsonPolicy::deserializeImpl(json, *derived());
|
||||
}
|
||||
|
||||
//! \copydoc CValueObject::toQVariant()
|
||||
virtual QVariant toQVariant() const override
|
||||
//! Virtual method to return QVariant, used with DBus QVariant lists
|
||||
virtual QVariant toQVariant() const
|
||||
{
|
||||
return maybeToQVariant(IsRegisteredQMetaType<Derived>());
|
||||
}
|
||||
|
||||
//! \copydoc CValueObject::convertFromQVariant
|
||||
virtual void convertFromQVariant(const QVariant &variant) override
|
||||
//! Set from QVariant
|
||||
virtual void convertFromQVariant(const QVariant &variant)
|
||||
{
|
||||
return maybeConvertFromQVariant(variant, IsRegisteredQMetaType<Derived>());
|
||||
}
|
||||
@@ -348,7 +358,7 @@ namespace BlackMisc
|
||||
virtual void setPropertyByIndex(const CVariant &variant, const CPropertyIndex &index) { PropertyIndexPolicy::setPropertyByIndex(*derived(), variant, index); }
|
||||
|
||||
//! Property by index
|
||||
virtual CVariant propertyByIndex(const BlackMisc::CPropertyIndex &index) const { CVariant result; PropertyIndexPolicy::propertyByIndex(*derived(), index, result); return result; }
|
||||
virtual CVariant propertyByIndex(const BlackMisc::CPropertyIndex &index) const; // implemented later due to cyclic include dependency
|
||||
|
||||
//! Property by index as String
|
||||
virtual QString propertyByIndexAsString(const CPropertyIndex &index, bool i18n = false) const { return PropertyIndexPolicy::propertyByIndexAsString(*derived(), index, i18n); }
|
||||
@@ -363,6 +373,9 @@ namespace BlackMisc
|
||||
}
|
||||
|
||||
protected:
|
||||
template <typename T>
|
||||
friend struct Private::CValueObjectMetaInfo;
|
||||
|
||||
//! Default constructor.
|
||||
CValueObjectStdTuple() = default;
|
||||
|
||||
@@ -377,40 +390,37 @@ namespace BlackMisc
|
||||
//! Copy assignment operator.
|
||||
CValueObjectStdTuple &operator =(const CValueObjectStdTuple &) = default;
|
||||
|
||||
//! \copydoc CValueObject::getMetaTypeId
|
||||
virtual int getMetaTypeId() const override
|
||||
//! String for streaming operators
|
||||
virtual QString stringForStreaming() const { return this->convertToQString(); }
|
||||
|
||||
//! Returns the Qt meta type ID of this object.
|
||||
virtual int getMetaTypeId() const
|
||||
{
|
||||
return maybeGetMetaTypeId(IsRegisteredQMetaType<Derived>());
|
||||
}
|
||||
|
||||
//! \copydoc CValueObject::isA
|
||||
virtual bool isA(int metaTypeId) const override
|
||||
/*!
|
||||
* Returns true if this object is an instance of the class with the given meta type ID,
|
||||
* or one of its subclasses.
|
||||
*/
|
||||
virtual bool isA(int metaTypeId) const
|
||||
{
|
||||
if (metaTypeId == QMetaType::UnknownType) { return false; }
|
||||
if (metaTypeId == maybeGetMetaTypeId(IsRegisteredQMetaType<Derived>())) { return true; }
|
||||
return Base::isA(metaTypeId);
|
||||
return BaseOrDummy::isA(metaTypeId);
|
||||
}
|
||||
|
||||
//! \copydoc CValueObject::compareImpl
|
||||
virtual int compareImpl(const CValueObject &other) const override
|
||||
//! Marshall to DBus
|
||||
virtual void marshallToDbus(QDBusArgument &argument) const
|
||||
{
|
||||
const auto &otherDerived = static_cast<const Derived &>(other);
|
||||
int result = ComparePolicy::compareImpl(*derived(), otherDerived);
|
||||
if (result) return result;
|
||||
return Base::compareImpl(other);
|
||||
}
|
||||
|
||||
//! \copydoc CValueObject::marshallToDbus()
|
||||
virtual void marshallToDbus(QDBusArgument &argument) const override
|
||||
{
|
||||
Base::marshallToDbus(argument);
|
||||
BaseOrDummy::marshallToDbus(argument);
|
||||
DBusPolicy::marshallImpl(argument, *derived());
|
||||
}
|
||||
|
||||
//! \copydoc CValueObject::unmarshallFromDbus()
|
||||
virtual void unmarshallFromDbus(const QDBusArgument &argument) override
|
||||
//! Unmarshall from DBus
|
||||
virtual void unmarshallFromDbus(const QDBusArgument &argument)
|
||||
{
|
||||
Base::unmarshallFromDbus(argument);
|
||||
BaseOrDummy::unmarshallFromDbus(argument);
|
||||
DBusPolicy::unmarshallImpl(argument, *derived());
|
||||
}
|
||||
|
||||
@@ -428,82 +438,119 @@ namespace BlackMisc
|
||||
void maybeConvertFromQVariant(const QVariant &variant, std::false_type) { Q_UNUSED(variant); }
|
||||
};
|
||||
|
||||
/*!
|
||||
* Non-member non-friend operator for streaming T objects to QDBusArgument.
|
||||
* Needed because we can't rely on the friend operator in some cases due to
|
||||
* an unrelated template for streaming Container<T> in QtDBus/qdbusargument.h
|
||||
* which matches more types than it can actually handle.
|
||||
*/
|
||||
template <class T> typename std::enable_if<std::is_base_of<CValueObject, T>::value, QDBusArgument>::type const &
|
||||
operator>>(const QDBusArgument &argument, T &valueObject)
|
||||
{
|
||||
return argument >> static_cast<CValueObject &>(valueObject);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Non-member non-friend operator for streaming T objects from QDBusArgument.
|
||||
* Needed because we can't rely on the friend operator in some cases due to
|
||||
* an unrelated template for streaming Container<T> in QtDBus/qdbusargument.h
|
||||
* which matches more types than it can actually handle.
|
||||
*/
|
||||
template <class T> typename std::enable_if<std::is_base_of<CValueObject, T>::value, QDBusArgument>::type &
|
||||
operator<<(QDBusArgument &argument, const T &valueObject)
|
||||
{
|
||||
return argument << static_cast<CValueObject const &>(valueObject);
|
||||
}
|
||||
|
||||
//! Non member, non friend operator >> for JSON
|
||||
inline const QJsonObject &operator>>(const QJsonObject &json, CValueObject &valueObject)
|
||||
{
|
||||
valueObject.convertFromJson(json);
|
||||
return json;
|
||||
}
|
||||
|
||||
//! Non member, non friend operator >> for JSON
|
||||
inline const QJsonValue &operator>>(const QJsonValue &json, CValueObject &valueObject)
|
||||
{
|
||||
valueObject.convertFromJson(json.toObject());
|
||||
return json;
|
||||
}
|
||||
|
||||
//! Non member, non friend operator >> for JSON
|
||||
inline const QJsonValueRef &operator>>(const QJsonValueRef &json, CValueObject &valueObject)
|
||||
{
|
||||
valueObject.convertFromJson(json.toObject());
|
||||
return json;
|
||||
}
|
||||
|
||||
//! Non member, non friend operator << for JSON
|
||||
inline QJsonArray &operator<<(QJsonArray &json, const CValueObject &value)
|
||||
{
|
||||
json.append(value.toJson());
|
||||
return json;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Non member, non friend operator << for JSON
|
||||
* \param json
|
||||
* \param value as pair name/value
|
||||
* \return
|
||||
*/
|
||||
template <class T> typename std::enable_if<std::is_base_of<CValueObject, T>::value, QJsonObject>::type &
|
||||
operator<<(QJsonObject &json, const std::pair<QString, T> &value)
|
||||
{
|
||||
json.insert(value.first, QJsonValue(value.second.toJson()));
|
||||
return json;
|
||||
}
|
||||
|
||||
//! qHash overload, needed for storing CValueObject in a QSet.
|
||||
inline uint qHash(const BlackMisc::CValueObject &value, uint seed = 0)
|
||||
{
|
||||
return ::qHash(value.getValueHash(), seed);
|
||||
}
|
||||
|
||||
// Needed so that our qHash overload doesn't hide the qHash overloads in the global namespace.
|
||||
// This will be safe as long as no global qHash has the same signature as ours.
|
||||
// Alternative would be to qualify all our invokations of the global qHash as ::qHash.
|
||||
using ::qHash;
|
||||
|
||||
} // namespace
|
||||
|
||||
// TODO Includes due to cyclic dependencies can be removed when CValueObjectStdTuple 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
|
||||
{
|
||||
template <class Derived, class Base>
|
||||
CVariant CValueObjectStdTuple<Derived, Base>::toCVariant() const
|
||||
{
|
||||
return CVariant(this->toQVariant());
|
||||
}
|
||||
template <class Derived, class Base>
|
||||
void CValueObjectStdTuple<Derived, Base>::convertFromCVariant(const CVariant &variant)
|
||||
{
|
||||
this->convertFromQVariant(variant.getQVariant());
|
||||
}
|
||||
template <class Derived, class Base>
|
||||
CPropertyIndexList CValueObjectStdTuple<Derived, Base>::apply(const BlackMisc::CPropertyIndexVariantMap &indexMap, bool skipEqualValues)
|
||||
{
|
||||
CPropertyIndexList result;
|
||||
PropertyIndexPolicy::apply(*derived(), indexMap, result, skipEqualValues);
|
||||
return result;
|
||||
}
|
||||
template <class Derived, class Base>
|
||||
CVariant CValueObjectStdTuple<Derived, Base>::propertyByIndex(const BlackMisc::CPropertyIndex &index) const
|
||||
{
|
||||
CVariant result;
|
||||
PropertyIndexPolicy::propertyByIndex(*derived(), index, result);
|
||||
return result;
|
||||
}
|
||||
namespace Policy
|
||||
{
|
||||
namespace PropertyIndex
|
||||
{
|
||||
template <class T, class...>
|
||||
void Default::apply(T &obj, const CPropertyIndexVariantMap &indexMap, CPropertyIndexList &o_changed, bool skipEqualValues, Default::EnableIfEmptyBase<T>)
|
||||
{
|
||||
if (indexMap.isEmpty()) return;
|
||||
|
||||
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 = obj.equalsPropertyByIndex(value, index);
|
||||
if (equal) { continue; }
|
||||
}
|
||||
obj.setPropertyByIndex(value, index);
|
||||
o_changed.push_back(index);
|
||||
}
|
||||
}
|
||||
template <class T, class...>
|
||||
void Default::setPropertyByIndex(T &obj, const CVariant &variant, const CPropertyIndex &index, Default::EnableIfEmptyBase<T>)
|
||||
{
|
||||
if (index.isMyself())
|
||||
{
|
||||
obj.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 T, class...>
|
||||
void Default::propertyByIndex(const T &obj, const CPropertyIndex &index, CVariant &o_property, Default::EnableIfEmptyBase<T>)
|
||||
{
|
||||
if (index.isMyself())
|
||||
{
|
||||
o_property = obj.toCVariant();
|
||||
return;
|
||||
}
|
||||
using Base = CValueObjectStdTuple<T, typename T::base_type>;
|
||||
auto i = index.frontCasted<typename Base::ColumnIndex>();
|
||||
switch (i)
|
||||
{
|
||||
case Base::IndexIcon:
|
||||
o_property = CVariant::from(obj.toIcon());
|
||||
return;
|
||||
case Base::IndexPixmap:
|
||||
o_property = CVariant::from(obj.toPixmap());
|
||||
return;
|
||||
case Base::IndexString:
|
||||
o_property = CVariant(obj.toQString());
|
||||
return;
|
||||
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));
|
||||
}
|
||||
template <class T, class...>
|
||||
QString Default::propertyByIndexAsString(const T &obj, const CPropertyIndex &index, bool i18n, Default::EnableIfEmptyBase<T>)
|
||||
{
|
||||
// default implementation, requires propertyByIndex
|
||||
return obj.propertyByIndex(index).toQString(i18n);
|
||||
}
|
||||
template <class T, class...>
|
||||
bool Default::equalsPropertyByIndex(const T &obj, const CVariant &compareValue, const CPropertyIndex &index, Default::EnableIfEmptyBase<T>)
|
||||
{
|
||||
return obj.propertyByIndex(index) == compareValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // guard
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
namespace BlackMisc
|
||||
{
|
||||
class CEmpty;
|
||||
class CPropertyIndexList;
|
||||
class CPropertyIndexVariantMap;
|
||||
|
||||
@@ -58,7 +59,7 @@ namespace BlackMisc
|
||||
|
||||
private:
|
||||
template <class T>
|
||||
static void maybeRegisterMetaValueType() { maybeRegisterMetaValueType<T>(std::is_base_of<CValueObject, T>()); }
|
||||
static void maybeRegisterMetaValueType() { maybeRegisterMetaValueType<T>(std::is_base_of<CEmpty, T>()); } // FIXME use TemplateIsBaseOf
|
||||
template <class T>
|
||||
static void maybeRegisterMetaValueType(std::true_type) { BlackMisc::registerMetaValueType<T>(); }
|
||||
template <class T>
|
||||
@@ -115,7 +116,7 @@ namespace BlackMisc
|
||||
|
||||
private:
|
||||
template <class U> static bool baseEquals(const U &a, const U &b) { return a == b; }
|
||||
template <class U> static bool baseEquals(const CValueObject &, const CValueObject &) { return true; }
|
||||
template <class U> static bool baseEquals(const CEmpty &, const CEmpty &) { return true; }
|
||||
};
|
||||
};
|
||||
|
||||
@@ -192,28 +193,53 @@ namespace BlackMisc
|
||||
|
||||
namespace Compare
|
||||
{
|
||||
//! CValueObjectStdTuple policy for a class without polymorphic comparison support
|
||||
//! CValueObjectStdTuple policy for a class without compare() support
|
||||
struct None
|
||||
{
|
||||
//! Policy implementation of CValueObject::compareImpl
|
||||
template <class T, class...>
|
||||
static bool compareImpl(const T &, const T &) { return 0; }
|
||||
//! Inner class template which actually bestows the friend function via the Barton-Nackman trick
|
||||
template <class T, class>
|
||||
struct Ops {};
|
||||
};
|
||||
|
||||
//! CValueObjectStdTuple polymorphic comparison policy which inherits the policy of the base class
|
||||
//! CValueObjectStdTuple compare() policy which inherits the policy of the base class
|
||||
struct Inherit
|
||||
{
|
||||
//! Policy implementation of CValueObject::compareImpl
|
||||
template <class T, class Base = T>
|
||||
static bool compareImpl(const T &a, const T &b) { return Private::Inherit<Base>::Compare::template compareImpl<T, typename Base::base_type>(a, b); }
|
||||
//! Inner class template which actually bestows the operators via the Barton-Nackman trick
|
||||
template <class T, class Base>
|
||||
struct Ops : public CValueObjectStdTuplePolicy<Base>::Compare::template Ops<T, typename Base::base_type> {};
|
||||
};
|
||||
|
||||
//! CValueObjectStdTuple policy for a class with default metatuple-based polymorphic comparison support
|
||||
struct MetaTuple : private Private::EncapsulationBreaker
|
||||
//! CValueObjectStdTuple policy for a class with default metatuple-based compare() support
|
||||
struct MetaTuple
|
||||
{
|
||||
//! Policy implementation of CValueObject::compareImpl
|
||||
template <class T, class...>
|
||||
static bool compareImpl(const T &a, const T &b) { return compare(Private::EncapsulationBreaker::toMetaTuple(a), Private::EncapsulationBreaker::toMetaTuple(b)); }
|
||||
//! Inner class template which actually bestows the operators via the Barton-Nackman trick
|
||||
template <class T, class>
|
||||
struct Ops : private Private::EncapsulationBreaker
|
||||
{
|
||||
//! compare friend function
|
||||
friend int compare(const T &a, const T &b)
|
||||
{
|
||||
int result = BlackMisc::compare(Private::EncapsulationBreaker::toMetaTuple(a), Private::EncapsulationBreaker::toMetaTuple(b));
|
||||
return result ? result : compare(static_cast<const typename T::base_type &>(a), static_cast<const typename T::base_type &>(b));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
//! CValueObjectStdTuple policy for a class which implements compare() in terms of the less than operator
|
||||
struct LessThan
|
||||
{
|
||||
//! Inner class template which actually bestows the operators via the Barton-Nackman trick
|
||||
template <class T, class>
|
||||
struct Ops
|
||||
{
|
||||
//! compare friend function
|
||||
friend int compare(const T &a, const T &b)
|
||||
{
|
||||
if (a < b) { return -1; }
|
||||
if (b < a) { return 1; }
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -372,24 +398,7 @@ namespace BlackMisc
|
||||
template <class T, class...>
|
||||
static void apply(T &obj, const CPropertyIndexVariantMap &indexMap, CPropertyIndexList &o_changed, bool skipEqualValues, DisableIfEmptyBase<T> = nullptr) { o_changed = obj.T::base_type::apply(indexMap, skipEqualValues); }
|
||||
template <class T, class...>
|
||||
static void apply(T &obj, const CPropertyIndexVariantMap &indexMap, CPropertyIndexList &o_changed, bool skipEqualValues, EnableIfEmptyBase<T> = nullptr)
|
||||
{
|
||||
if (indexMap.isEmpty()) return;
|
||||
|
||||
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 = obj.equalsPropertyByIndex(value, index);
|
||||
if (equal) { continue; }
|
||||
}
|
||||
obj.setPropertyByIndex(value, index);
|
||||
o_changed.push_back(index);
|
||||
}
|
||||
}
|
||||
static void apply(T &obj, const CPropertyIndexVariantMap &indexMap, CPropertyIndexList &o_changed, bool skipEqualValues, EnableIfEmptyBase<T> = nullptr); // implemented in valueobject.h due to cyclic include dependency
|
||||
//! @}
|
||||
|
||||
//! \copydoc CValueObjectStdTuple::setPropertyByIndex
|
||||
@@ -397,18 +406,7 @@ namespace BlackMisc
|
||||
template <class T, class...>
|
||||
static void setPropertyByIndex(T &obj, const CVariant &variant, const CPropertyIndex &index, DisableIfEmptyBase<T> = nullptr) { return obj.T::base_type::setPropertyByIndex(variant, index); }
|
||||
template <class T, class...>
|
||||
static void setPropertyByIndex(T &obj, const CVariant &variant, const CPropertyIndex &index, EnableIfEmptyBase<T> = nullptr)
|
||||
{
|
||||
if (index.isMyself())
|
||||
{
|
||||
obj.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));
|
||||
}
|
||||
static void setPropertyByIndex(T &obj, const CVariant &variant, const CPropertyIndex &index, EnableIfEmptyBase<T> = nullptr); // implemented in valueobject.h due to cyclic include dependency
|
||||
//! @}
|
||||
|
||||
//! \copydoc CValueObjectStdTuple::propertyByIndex
|
||||
@@ -416,34 +414,7 @@ namespace BlackMisc
|
||||
template <class T, class...>
|
||||
static void propertyByIndex(const T &obj, const CPropertyIndex &index, CVariant &o_property, DisableIfEmptyBase<T> = nullptr) { o_property = obj.T::base_type::propertyByIndex(index); }
|
||||
template <class T, class...>
|
||||
static void propertyByIndex(const T &obj, const CPropertyIndex &index, CVariant &o_property, EnableIfEmptyBase<T> = nullptr)
|
||||
{
|
||||
if (index.isMyself())
|
||||
{
|
||||
o_property = obj.toCVariant();
|
||||
return;
|
||||
}
|
||||
using Base = CValueObjectStdTuple<T, typename T::base_type>;
|
||||
auto i = index.frontCasted<typename Base::ColumnIndex>();
|
||||
switch (i)
|
||||
{
|
||||
case Base::IndexIcon:
|
||||
o_property = CVariant::from(obj.toIcon());
|
||||
break;
|
||||
case Base::IndexPixmap:
|
||||
o_property = CVariant::from(obj.toPixmap());
|
||||
break;
|
||||
case Base::IndexString:
|
||||
o_property = CVariant(obj.toQString());
|
||||
break;
|
||||
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));
|
||||
}
|
||||
static void propertyByIndex(const T &obj, const CPropertyIndex &index, CVariant &o_property, EnableIfEmptyBase<T> = nullptr); // implemented in valueobject.h due to cyclic include dependency
|
||||
//! @}
|
||||
|
||||
//! \copydoc CValueObjectStdTuple::propertyByIndexAsString
|
||||
@@ -451,11 +422,7 @@ namespace BlackMisc
|
||||
template <class T, class...>
|
||||
static QString propertyByIndexAsString(const T &obj, const CPropertyIndex &index, bool i18n, DisableIfEmptyBase<T> = nullptr) { return obj.T::base_type::propertyByIndexAsString(index, i18n); }
|
||||
template <class T, class...>
|
||||
static QString propertyByIndexAsString(const T &obj, const CPropertyIndex &index, bool i18n, EnableIfEmptyBase<T> = nullptr)
|
||||
{
|
||||
// default implementation, requires propertyByIndex
|
||||
return obj.propertyByIndex(index).toQString(i18n);
|
||||
}
|
||||
static QString propertyByIndexAsString(const T &obj, const CPropertyIndex &index, bool i18n, EnableIfEmptyBase<T> = nullptr); // implemented in valueobject.h due to cyclic include dependency
|
||||
//! @}
|
||||
|
||||
//! \copydoc CValueObjectStdTuple::equalsPropertyByIndex
|
||||
@@ -463,10 +430,7 @@ namespace BlackMisc
|
||||
template <class T, class...>
|
||||
static bool equalsPropertyByIndex(const T &obj, const CVariant &compareValue, const CPropertyIndex &index, DisableIfEmptyBase<T> = nullptr) { return obj.T::base_type::equalsPropertyByIndex(compareValue, index); }
|
||||
template <class T, class...>
|
||||
static bool equalsPropertyByIndex(const T &obj, const CVariant &compareValue, const CPropertyIndex &index, EnableIfEmptyBase<T> = nullptr)
|
||||
{
|
||||
return obj.propertyByIndex(index) == compareValue;
|
||||
}
|
||||
static bool equalsPropertyByIndex(const T &obj, const CVariant &compareValue, const CPropertyIndex &index, EnableIfEmptyBase<T> = nullptr); // implemented in valueobject.h due to cyclic include dependency
|
||||
//! @}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -12,16 +12,14 @@
|
||||
#ifndef BLACKMISC_VALUEOBJECT_PRIVATE_H
|
||||
#define BLACKMISC_VALUEOBJECT_PRIVATE_H
|
||||
|
||||
#include <QString>
|
||||
#include <QMetaType>
|
||||
#include <QDBusArgument>
|
||||
#include <QJsonObject>
|
||||
|
||||
namespace BlackMisc
|
||||
{
|
||||
class CValueObject;
|
||||
|
||||
template <class T> typename std::enable_if<std::is_base_of<CValueObject, T>::value, QDBusArgument>::type const &
|
||||
operator>>(const QDBusArgument &argument, T &valueObject);
|
||||
class CEmpty;
|
||||
class CVariant;
|
||||
class CPropertyIndex;
|
||||
|
||||
@@ -41,7 +39,7 @@ namespace BlackMisc
|
||||
virtual uint getValueHash(const void *object) const = 0;
|
||||
virtual int getMetaTypeId() const = 0;
|
||||
virtual const void *upCastTo(const void *object, int metaTypeId) const = 0;
|
||||
virtual int compare(const void *lhs, const void *rhs) const = 0;
|
||||
virtual int compareImpl(const void *lhs, const void *rhs) const = 0;
|
||||
virtual void setPropertyByIndex(void *object, const CVariant &variant, const CPropertyIndex &index) const = 0;
|
||||
virtual void propertyByIndex(const void *object, CVariant &o_variant, const BlackMisc::CPropertyIndex &index) const = 0;
|
||||
virtual QString propertyByIndexAsString(const void *object, const CPropertyIndex &index, bool i18n) const = 0;
|
||||
@@ -66,8 +64,7 @@ namespace BlackMisc
|
||||
}
|
||||
virtual void unmarshall(const QDBusArgument &arg, void *object) const override
|
||||
{
|
||||
// FIXME Using the usual >> operator syntax here attempts to call the Container<T> overload in QtDBus/qdbusargument.h
|
||||
BlackMisc::operator >>(arg, cast(object));
|
||||
arg >> cast(object);
|
||||
}
|
||||
virtual uint getValueHash(const void *object) const override
|
||||
{
|
||||
@@ -82,7 +79,10 @@ namespace BlackMisc
|
||||
const auto base = static_cast<const void *>(static_cast<const typename T::base_type *>(&cast(object)));
|
||||
return metaTypeId == getMetaTypeId() ? object : CValueObjectMetaInfo<typename T::base_type>{}.upCastTo(base, metaTypeId);
|
||||
}
|
||||
virtual int compare(const void *lhs, const void *rhs) const override; // FIXME defined out-of-line in valueobject.h because it uses CValueObject
|
||||
virtual int compareImpl(const void *lhs, const void *rhs) const override
|
||||
{
|
||||
return compare(cast(lhs), cast(rhs));
|
||||
}
|
||||
virtual void setPropertyByIndex(void *object, const CVariant &variant, const CPropertyIndex &index) const override
|
||||
{
|
||||
cast(object).setPropertyByIndex(variant, index);
|
||||
@@ -109,10 +109,13 @@ namespace BlackMisc
|
||||
|
||||
//! \private Explicit specialization for the terminating case of the recursive CValueObjectMetaInfo::upCastTo.
|
||||
template <>
|
||||
inline const void *CValueObjectMetaInfo<CValueObject>::upCastTo(const void *, int) const
|
||||
struct CValueObjectMetaInfo<CEmpty>
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
const void *upCastTo(const void *, int) const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
//! \private Getter to obtain the IValueObjectMetaInfo which was stored by BlackMisc::registerMetaValueType.
|
||||
IValueObjectMetaInfo *getValueObjectMetaInfo(int typeId);
|
||||
|
||||
@@ -9,10 +9,11 @@
|
||||
|
||||
//! \file
|
||||
|
||||
#include "valueobject.h" // outside include guard due to cyclic dependency hack (MS)
|
||||
|
||||
#ifndef BLACKMISC_VARIANT_H
|
||||
#define BLACKMISC_VARIANT_H
|
||||
|
||||
#include "valueobject.h"
|
||||
#include <QVariant>
|
||||
#include <QDateTime>
|
||||
#include <QJsonValueRef>
|
||||
@@ -220,7 +221,7 @@ namespace BlackMisc
|
||||
};
|
||||
|
||||
//! Compare stored value of CVariant with any CValueObject derived class.
|
||||
template <class T, class = typename std::enable_if<std::is_base_of<CValueObject, T>::value>::type>
|
||||
template <class T, class = typename std::enable_if<IsValueObject<T>::value>::type>
|
||||
bool operator ==(const T &value, const CVariant &variant)
|
||||
{
|
||||
if (variant.canConvert<T>()) { return variant.value<T>() == value; }
|
||||
@@ -228,21 +229,21 @@ namespace BlackMisc
|
||||
}
|
||||
|
||||
//! Compare stored value of CVariant with any CValueObject derived class.
|
||||
template <class T, class = typename std::enable_if<std::is_base_of<CValueObject, T>::value>::type>
|
||||
template <class T, class = typename std::enable_if<IsValueObject<T>::value>::type>
|
||||
bool operator !=(const T &value, const CVariant &variant)
|
||||
{
|
||||
return !(value == variant);
|
||||
}
|
||||
|
||||
//! Compare stored value of CVariant with any CValueObject derived class.
|
||||
template <class T, class = typename std::enable_if<std::is_base_of<CValueObject, T>::value>::type>
|
||||
template <class T, class = typename std::enable_if<IsValueObject<T>::value>::type>
|
||||
bool operator ==(const CVariant &variant, const T &value)
|
||||
{
|
||||
return value == variant;
|
||||
}
|
||||
|
||||
//! Compare stored value of CVariant with any CValueObject derived class.
|
||||
template <class T, class = typename std::enable_if<std::is_base_of<CValueObject, T>::value>::type>
|
||||
template <class T, class = typename std::enable_if<IsValueObject<T>::value>::type>
|
||||
bool operator !=(const CVariant &variant, const T &value)
|
||||
{
|
||||
return !(value == variant);
|
||||
|
||||
Reference in New Issue
Block a user