using hashes to perform comparisons between blackmisc value objects stored inside of QVariant

refs #81
This commit is contained in:
Klaus Basan
2013-12-22 20:40:30 +00:00
committed by Mathew Sutcliffe
parent 67a5dbfe48
commit a280d239e6
22 changed files with 416 additions and 0 deletions

View File

@@ -58,6 +58,28 @@ namespace BlackMisc
return !((*this) == other);
}
/*
* Compare
*/
int CAltitude::compare(const QVariant &qv) const
{
Q_ASSERT(qv.canConvert<CAltitude>() || qv.canConvert<CLength>());
Q_ASSERT(qv.isValid() && !qv.isNull());
if (qv.canConvert<CAltitude>())
{
CAltitude other = qv.value<CAltitude>();
if (this->isMeanSeaLevel() && other.isAboveGroundLevel()) return 1;
if (this->isAboveGroundLevel() && other.isMeanSeaLevel()) return -1;
return this->compare(other);
}
else if (qv.canConvert<CLength>())
{
return this->compare(qv.value<CLength>());
}
qFatal("Invalid comparison");
return 0; // just for compiler
}
/*
* Register metadata
*/

View File

@@ -122,6 +122,17 @@ namespace BlackMisc
return m_datum;
}
/*!
* \copydoc BlackObject::compare
*/
virtual int compare(const QVariant &qv) const;
/*!
* \todo this is a hack, to avoid hiding inherited names in CPhysicalQuantity
* (see Effective C++ item 33) CPhysicalQuantity::compare is the real culprit
*/
int compare(const CLength &other) const { return static_cast<const CLength *>(this)->compare(other); }
/*!
* \brief Register metadata
*/

View File

@@ -87,6 +87,15 @@ namespace BlackMisc
return QVariant::fromValue(*this);
}
/*!
* \brief Value hash
* \return
*/
virtual uint getValueHash() const
{
return CModulator::getValueHash();
}
/*!
* \brief Constructor
* \param name

View File

@@ -7,6 +7,7 @@
#include "blackmisc/aviocomsystem.h"
#include "blackmisc/avionavsystem.h"
#include "blackmisc/avioadfsystem.h"
#include "blackmisc/blackmiscfreefunctions.h"
using BlackMisc::PhysicalQuantities::CFrequency;
using BlackMisc::PhysicalQuantities::CFrequencyUnit;
@@ -76,6 +77,18 @@ namespace BlackMisc
argument >> this->m_digits;
}
/*
* Value hash
*/
template <class AVIO> uint CModulator<AVIO>::getValueHash() const
{
QList<uint> hashs;
hashs << this->m_frequencyActive.getValueHash();
hashs << this->m_frequencyStandby.getValueHash();
hashs << qHash(this->m_digits);
return BlackMisc::calculateHash(hashs, "CModulator");
}
// see here for the reason of thess forward instantiations
// http://www.parashift.com/c++-faq/separate-template-class-defn-from-decl.html
template class CModulator<CComSystem>;

View File

@@ -214,6 +214,12 @@ namespace BlackMisc
*/
virtual void unmarshallFromDbus(const QDBusArgument &argument);
/*!
* \brief Value hash
* \return
*/
virtual uint getValueHash() const;
public:
/*!
* \brief Virtual destructor

View File

@@ -4,6 +4,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "blackmisc/aviotransponder.h"
#include "blackmisc/blackmiscfreefunctions.h"
namespace BlackMisc
{
@@ -136,6 +137,18 @@ namespace BlackMisc
this->m_transponderMode = static_cast<TransponderMode>(tm);
}
/*
* Value hash
*/
uint CTransponder::getValueHash() const
{
QList<uint> hashs;
hashs << qHash(this->m_name);
hashs << qHash(this->m_transponderCode);
hashs << qHash(this->m_transponderMode);
return BlackMisc::calculateHash(hashs, "CTransponder");
}
/*
* Register metadata of unit and quantity
*/

View File

@@ -331,6 +331,12 @@ namespace BlackMisc
return CTransponder::tryGetTransponder(o_transponder, "Transponder", transponderCode, mode);
}
/*!
* \brief Value hash
* \return
*/
virtual uint getValueHash() const;
/*!
* \brief Register metadata of unit and quantity
*/

View File

@@ -81,6 +81,98 @@ void BlackMisc::initResources()
{
initBlackMiscResources();
}
/*
* Stupid extension bo be able to compare 2 QVariants
*/
bool BlackMisc::equalQVariants(const QVariant &v1, const QVariant &v2)
{
// prephase, shortcuts
if (v1 == v2) return true; // compares on primitives or on address
if (!v1.isValid() || !v2.isValid()) return false;
if (v1.type() != v2.type()) return false;
if (v1.userType() != v2.userType()) return false;
// I have same types now
const CValueObject *cs1 = CValueObject::fromQVariant(v1);
const CValueObject *cs2 = CValueObject::fromQVariant(v2);
if (cs1 && cs2)
{
uint h1 = cs1->getValueHash();
uint h2 = cs2->getValueHash();
return h1 == h2;
}
return false;
}
/*
* Compare values
*/
int BlackMisc:: compareQVariants(const QVariant &v1, const QVariant &v2)
{
if (v1 == v2) return 0; // compares on primitives or on address
if (!v1.isValid() || !v2.isValid()) qFatal("Invalid variants");
if (v1.type() != v2.type()) qFatal("Mismatching types");
if (v1.userType() != v2.userType()) qFatal("Mismatching user types");
switch (v1.type())
{
case QMetaType::QString:
{
QString s1 = v1.value<QString>();
QString s2 = v2.value<QString>();
return s1.compare(s2);
}
case QMetaType::QDateTime:
{
QDateTime dt1 = v1.value<QDateTime>();
QDateTime dt2 = v2.value<QDateTime>();
if (dt1 == dt2) return 0;
return dt1 < dt2 ? -1 : 1;
}
case QMetaType::QDate:
{
QDate d1 = v1.value<QDate>();
QDate d2 = v2.value<QDate>();
if (d1 == d2) return 0;
return d1 < d2 ? -1 : 1;
}
case QMetaType::QTime:
{
QTime t1 = v1.value<QTime>();
QTime t2 = v2.value<QTime>();
if (t1 == t2) return 0;
return t1 < t2 ? -1 : 1;
}
default:
break;
}
// BlackObject
if (v1.type() == QVariant::UserType)
{
const CValueObject *cs1 = CValueObject::fromQVariant(v1);
const CValueObject *cs2 = CValueObject::fromQVariant(v2);
if (cs1 && cs2)
{
return cs1->compare(v2); // Note, that I have to compare against QVariant
}
}
// all kind of numeric values
if (v1.canConvert<double>())
{
double d1 = v1.value<double>();
double d2 = v2.value<double>();
if (d1 == d2) return 0;
return d1 < d2 ? -1 : 1;
}
qFatal("Unknown type for compare");
return -1;
}
/*
* To string
*/
@@ -98,6 +190,27 @@ QString BlackMisc::qVariantToString(const QVariant &qv, bool i18n)
}
}
/*
* Add hash values
*/
uint BlackMisc::calculateHash(const QList<uint> &values, const char *className)
{
// http://stackoverflow.com/questions/113511/hash-code-implementation/113600#113600
if (values.isEmpty()) return 0;
uint hash = values.first();
for (int i = 1; i < values.size(); i++)
{
hash = 37 * hash + values.at(i);
}
// same values, but different class?
if (className)
{
hash = 37 * hash + qHash(QString(className));
}
return hash;
}
/*
* Fix QVariant if it comes from DBus and contains QDBusArgument
*/

View File

@@ -7,6 +7,7 @@
#define BLACKMISC_FREEFUNCTIONS_H
#include <QDir> // for Q_INIT_RESOURCE
#include <QList>
#include <QVariant>
#include <QDBusArgument>
@@ -81,6 +82,21 @@ namespace BlackMisc
*/
void initResources();
/*!
* \brief Compare 2 QVariants
* \param v1
* \param v2
*/
bool equalQVariants(const QVariant &v1, const QVariant &v2);
/*!
* \brief Compare QVariants
* \param v1
* \param v2
* \return
*/
int compareQVariants(const QVariant &v1, const QVariant &v2);
/*!
* \brief QVariant to string, allows to stringify CValueObject
* \param qv
@@ -109,6 +125,14 @@ namespace BlackMisc
// TODO: To be removed if a better solution is found
QVariant complexQtTypeFromDbusArgument(const QDBusArgument &argument, int type);
/*!
* \brief Add several hash values
* \param values
* \param classTypeId
* \return
*/
uint calculateHash(const QList<uint> &values, const char *className);
} // BlackMisc
#endif // guard

View File

@@ -4,6 +4,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "blackmisc/coordinategeodetic.h"
#include "blackmisc/blackmiscfreefunctions.h"
namespace BlackMisc
{
@@ -67,6 +68,18 @@ namespace BlackMisc
qDBusRegisterMetaType<CCoordinateGeodetic>();
}
/*
* Hash
*/
uint CCoordinateGeodetic::getValueHash() const
{
QList<uint> hashs;
hashs << this->m_latitude.getValueHash();
hashs << this->m_longitude.getValueHash();
hashs << this->m_height.getValueHash();
return BlackMisc::calculateHash(hashs, "CCoordinateGeodetic");
}
/*
* Great circle distance

View File

@@ -217,6 +217,11 @@ namespace BlackMisc
*/
bool operator !=(const CCoordinateGeodetic &other) const;
/*!
* \brief value Hash
*/
virtual uint getValueHash() const;
/*!
* Register metadata
*/

View File

@@ -71,6 +71,18 @@ namespace BlackMisc
return LATorLON(a);
}
/*
* Compare
*/
template <class LATorLON> int CEarthAngle<LATorLON>::compare(const QVariant &qv) const
{
Q_ASSERT(qv.canConvert<LATorLON>() || qv.canConvert<CAngle>());
Q_ASSERT(qv.isValid() && !qv.isNull());
if (qv.canConvert<LATorLON>())
return this->toAngle().compare(qv.value<LATorLON>().toAngle());
else
return this->toAngle().compare(qv.value<CAngle>());
}
// see here for the reason of thess forward instantiations
// http://www.parashift.com/c++-faq/separate-template-class-defn-from-decl.html

View File

@@ -184,6 +184,11 @@ namespace BlackMisc
return BlackMisc::PhysicalQuantities::CAngle(static_cast<BlackMisc::PhysicalQuantities::CAngle>(*this));
}
/*!
* Compare
*/
int compare(const QVariant &qv) const;
/*!
* Register metadata
*/

View File

@@ -138,6 +138,23 @@ namespace BlackMisc
return s;
}
/*
* Hash
*/
template <class ImplMatrix, int Rows, int Columns> uint CMatrixBase<ImplMatrix, Rows, Columns>::getValueHash() const
{
const QList<double> l = this->toList();
QList<uint> hashs;
// there is an issue with the signature of QList, so I use
// individual values
foreach(double v, l)
{
hashs << qHash(static_cast<long>(v));
}
return BlackMisc::calculateHash(hashs, "CMatrixBase");
}
/*
* To DBus
*/

View File

@@ -89,6 +89,12 @@ namespace BlackMisc
*/
QList<double> toList() const;
/*!
* \brief Value hash
* \return
*/
virtual uint getValueHash() const;
/*!
* \brief List of values
* \return

View File

@@ -144,6 +144,18 @@ namespace BlackMisc
argument >> this->m_k;
}
/*!
* \brief Stream to DBus
* \param argument
*/
template <class ImplVector> uint CVector3DBase<ImplVector>::getValueHash() const
{
QList<uint> hashs;
hashs << qHash(static_cast<long>(this->m_i));
hashs << qHash(static_cast<long>(this->m_j));
hashs << qHash(static_cast<long>(this->m_k));
return BlackMisc::calculateHash(hashs, "CVector3DBase");
}
/*
* Register metadata

View File

@@ -323,6 +323,12 @@ namespace BlackMisc
*/
CMatrix3x1 toMatrix3x1() const;
/*!
* \brief Value hash
* \return
*/
virtual uint getValueHash() const;
/*!
* \brief length / magnitude
* \return

View File

@@ -16,6 +16,7 @@
#include <QDebug>
#include <QSharedData>
#include <QSharedDataPointer>
#include <QHash>
namespace BlackMisc
{
@@ -301,6 +302,14 @@ namespace BlackMisc
return i18n ? QCoreApplication::translate("CMeasurementUnit", this->m_symbol.toStdString().c_str()) : this->m_symbol;
}
/*!
* \brief Value hash
* \return
*/
virtual uint getValueHash() const
{
return qHash(this->getName());
}
/*!
* \brief Rounded value

View File

@@ -254,6 +254,36 @@ namespace BlackMisc
return this->valueRoundedWithUnit(this->getUnit(), -1, i18n);
}
/*
* Hash
*/
template <class MU, class PQ> uint CPhysicalQuantity<MU, PQ>::getValueHash() const
{
QList<uint> hashs;
hashs << this->m_unit.getValueHash();
hashs << qHash(static_cast<long>(this->m_value));
return BlackMisc::calculateHash(hashs, "PQ");
}
/*
* Compare
*/
template <class MU, class PQ> int CPhysicalQuantity<MU, PQ>::compare(const QVariant &qv) const
{
Q_ASSERT(qv.canConvert<PQ>());
Q_ASSERT(!qv.isNull() && qv.isValid());
return this->compare(qv.value<PQ>());
}
/*
* Compare
*/
template <class MU, class PQ> int CPhysicalQuantity<MU, PQ>::compare(const PQ &other) const
{
if (other == (*this)) return 0;
return ((*this) < other) ? -1 : 1;
}
// see here for the reason of thess forward instantiations
// http://www.parashift.com/c++-faq/separate-template-class-defn-from-decl.html
template class CPhysicalQuantity<CLengthUnit, CLength>;

View File

@@ -347,11 +347,26 @@ namespace BlackMisc
*/
virtual void unmarshallFromDbus(const QDBusArgument &argument);
/*!
* \brief Value hash
* \return
*/
virtual uint getValueHash() const;
/*!
* \brief Register metadata of unit and quantity
*/
static void registerMetadata();
/*!
* \copydoc BlackObject::compare
*/
virtual int compare(const QVariant &qv) const;
/*!
* \copydoc BlackObject::compare
*/
virtual int compare(const PQ &other) const;
};
} // namespace

View File

@@ -60,6 +60,16 @@ namespace BlackMisc
return vo;
}
/*
* Compare
*/
int CValueObject::compare(const QVariant & /** qv **/) const
{
// not all classes have to implement this
qFatal("Property by index as string not implemented");
return -1; // avoid compiler warning
}
/*
* from DBus
*/

View File

@@ -12,6 +12,8 @@
namespace BlackMisc
{
// forward declaration
class CValueMap;
/*!
* \brief Base class for value objects.
@@ -132,6 +134,21 @@ namespace BlackMisc
*/
std::string toStdString(bool i18n = false) const;
/*!
* \brief Value hash, allows comparisons between QVariants
* \return
*/
virtual uint getValueHash() const = 0;
/*!
* Compares with QVariant with this object
* and returns an integer less than, equal to, or greater than zero
* if this is less than, equal to, or greater than QVariant.
* \remarks allows sorting among QVariants, not all classes implement this
* \return
*/
virtual int compare(const QVariant &qv) const;
/*!
* \brief Virtual method to return QVariant, used with DBUS QVariant lists
* \return
@@ -216,6 +233,48 @@ namespace BlackMisc
return argument << static_cast<CValueObject const &>(uc);
}
/*!
* Allow comparison with QVariant, e.g.
* QVariant == CFrequency ?
*/
template <class T> typename std::enable_if<std::is_base_of<CValueObject, T>::value, bool>::type
operator==(const QVariant &variant, const T &uc)
{
if (!variant.canConvert<T>()) return false;
T vuc = variant.value<T>();
return vuc == uc;
}
/*!
* Allow comparison with QVariant, e.g.
* QVariant != CFrequency ?
*/
template <class T> typename std::enable_if<std::is_base_of<CValueObject, T>::value, bool>::type
operator!=(const QVariant &variant, const T &uc)
{
return !(variant == uc);
}
/*!
* Allow comparison with QVariant, e.g.
* QVariant == CFrequency ?
*/
template <class T> typename std::enable_if<std::is_base_of<CValueObject, T>::value, bool>::type
operator==(const T &uc, const QVariant &variant)
{
return variant == uc;
}
/*!
* Allow comparison with QVariant, e.g.
* QVariant != CFrequency ?
*/
template <class T> typename std::enable_if<std::is_base_of<CValueObject, T>::value, bool>::type
operator!=(const T &uc, const QVariant &variant)
{
return variant != uc;
}
} // namespace
#endif // guard