Issue #15 CVariant can convert between CVariantList and our other container classes

This commit is contained in:
Mat Sutcliffe
2019-02-26 01:51:11 +00:00
parent e8a0a0b6ba
commit 07f6c8b73c
11 changed files with 212 additions and 13 deletions

View File

@@ -25,6 +25,10 @@ namespace BlackMisc
CSequence<CAircraftIcaoCode>(other)
{ }
CAircraftIcaoCodeList::CAircraftIcaoCodeList(std::initializer_list<CAircraftIcaoCode> il) :
CSequence<CAircraftIcaoCode>(il)
{ }
CAircraftIcaoCodeList CAircraftIcaoCodeList::findByDesignator(const QString &designator, int fuzzySearch) const
{
if (!fuzzySearch && !CAircraftIcaoCode::isValidDesignator(designator)) { return CAircraftIcaoCodeList(); }

View File

@@ -45,6 +45,9 @@ namespace BlackMisc
//! Construct from a base class object.
CAircraftIcaoCodeList(const CSequence<CAircraftIcaoCode> &other);
//! Construct from initializer list.
CAircraftIcaoCodeList(std::initializer_list<CAircraftIcaoCode> il);
//! Find by designator
CAircraftIcaoCodeList findByDesignator(const QString &designator, int fuzzySearch = -1) const;

View File

@@ -33,6 +33,10 @@ namespace BlackMisc
CSequence<CAirlineIcaoCode>(other)
{ }
CAirlineIcaoCodeList::CAirlineIcaoCodeList(std::initializer_list<CAirlineIcaoCode> il) :
CSequence<CAirlineIcaoCode>(il)
{ }
CAirlineIcaoCodeList CAirlineIcaoCodeList::findByDesignator(const QString &designator) const
{
if (!CAirlineIcaoCode::isValidAirlineDesignator(designator)) { return CAirlineIcaoCodeList(); }

View File

@@ -48,6 +48,9 @@ namespace BlackMisc
//! Construct from a base class object.
CAirlineIcaoCodeList(const CSequence<CAirlineIcaoCode> &other);
//! Construct from initializer list.
CAirlineIcaoCodeList(std::initializer_list<CAirlineIcaoCode> il);
//! Find by designator
//! Not unique because of virtual airlines
CAirlineIcaoCodeList findByDesignator(const QString &designator) const;

View File

@@ -80,6 +80,12 @@ namespace BlackMisc
//! Destructor.
~CSequence() = default;
//! Copy of internal vector.
//! @{
QVector<T> toVector() const & { return m_impl; }
QVector<T> toVector() && { return std::move(m_impl); }
//! @}
//! Returns iterator at the beginning of the sequence.
iterator begin() { return m_impl.begin(); }

View File

@@ -14,6 +14,7 @@
#include "blackmisc/logmessage.h"
#include "blackmisc/propertyindex.h"
#include "blackmisc/statusmessage.h"
#include "blackmisc/variantlist.h"
#include "blackmisc/variant.h"
#include <QByteArray>
@@ -42,6 +43,58 @@ namespace BlackMisc
return v.value<IValueObjectMetaInfo *>();
}
bool CVariant::canConvert(int typeId) const
{
if (m_v.canConvert(typeId))
{
return true;
}
if (typeId == qMetaTypeId<CVariantList>())
{
return m_v.canConvert<QVector<CVariant>>() || m_v.canConvert<QVariantList>();
}
if (userType() == qMetaTypeId<CVariantList>())
{
return QVariant::fromValue(QVector<CVariant>()).canConvert(typeId)
|| QVariant(typeId, nullptr).canConvert<QVariantList>();
}
return false;
}
bool CVariant::convert(int typeId)
{
if (!m_v.canConvert(typeId))
{
if (!canConvert(typeId)) { return false; }
if (typeId == qMetaTypeId<CVariantList>())
{
if (m_v.canConvert<QVector<CVariant>>())
{
if (!m_v.convert(qMetaTypeId<QVector<CVariant>>())) { return false; }
}
else if (m_v.canConvert<QVariantList>())
{
m_v.setValue(CVariantList(m_v.value<QSequentialIterable>()));
}
else { return false; }
}
if (userType() == qMetaTypeId<CVariantList>())
{
if (QVariant::fromValue(QVector<CVariant>()).canConvert(typeId))
{
if (!m_v.convert(qMetaTypeId<QVector<CVariant>>())) { return false; }
}
else { return false; }
}
}
return m_v.convert(typeId);
}
bool CVariant::isVariantList() const
{
return userType() == qMetaTypeId<CVariantList>();
}
QString CVariant::convertToQString(bool i18n) const
{
auto *meta = getValueObjectMetaInfo();

View File

@@ -18,6 +18,7 @@
#include "blackmisc/datastream.h"
#include "blackmisc/inheritancetraits.h"
#include "blackmisc/json.h"
#include "blackmisc/range.h"
#include "blackmisc/stringutils.h"
#include "blackmisc/variantprivate.h"
@@ -36,6 +37,7 @@ namespace BlackMisc
{
class CIcon;
class CPropertyIndex;
class CVariantList;
namespace Mixin
{
@@ -127,6 +129,8 @@ namespace BlackMisc
public Mixin::JsonOperators<CVariant>,
public Mixin::String<CVariant>
{
template <typename> struct tag {};
public:
//! Default constructor.
CVariant() {}
@@ -212,10 +216,10 @@ namespace BlackMisc
template <typename T> void set(T &&value) { m_v.setValue(std::forward<T>(value)); }
//! Return the value converted to the type T.
template <typename T> T value() const { return m_v.value<T>(); }
template <typename T> T value() const { return to(tag<T>()); }
//! Synonym for value().
template <typename T> T to() const { return m_v.value<T>(); }
template <typename T> T to() const { return to(tag<T>()); }
//! Returns the value converted to the type T, or a default if it can not be converted.
//! \details Parameter is passed by value to avoid odr-using the argument in case it is
@@ -226,13 +230,13 @@ namespace BlackMisc
const QVariant &getQVariant() const { return m_v; }
//! True if this variant can be converted to the type with the given metatype ID.
bool canConvert(int typeId) const { return m_v.canConvert(typeId); }
bool canConvert(int typeId) const;
//! True if this variant can be converted to the type T.
template <typename T> bool canConvert() const { return m_v.canConvert<T>(); }
template <typename T> bool canConvert() const { return canConvert(qMetaTypeId<T>()); }
//! Convert this variant to the type with the given metatype ID and return true if successful.
bool convert(int typeId) { return m_v.convert(typeId); }
bool convert(int typeId);
//! \copydoc BlackMisc::Mixin::String::toQString
QString convertToQString(bool i18n = false) const;
@@ -349,17 +353,53 @@ namespace BlackMisc
static int compareImpl(const CVariant &, const CVariant &);
uint getValueHash() const;
template <typename T> T to(tag<T>) const
{
auto copy = *this; copy.convert(qMetaTypeId<T>()); return *static_cast<const T*>(copy.data());
}
template <typename T> QList<T> to(tag<QList<T>>) const { return toImpl<QList<T>>(); }
template <typename T> QVector<T> to(tag<QVector<T>>) const { return toImpl<QVector<T>>(); }
template <typename T> CSequence<T> to(tag<CSequence<T>>) const { return toImpl<CSequence<T>>(); }
template <typename T> T toImpl() const
{
using VT = typename T::value_type;
T result;
if (isVariantList()) { for (const auto &v : m_v.value<QVector<CVariant>>()) { result.push_back(v.value<VT>()); } }
else { for (const auto &v : m_v.value<QSequentialIterable>()) { result.push_back(v.value<VT>()); } }
return result;
}
bool isVariantList() const;
template <typename F> static CVariant fromResultOfImpl(F &&func, std::true_type) { std::forward<F>(func)(); return {}; }
template <typename F> static CVariant fromResultOfImpl(F &&func, std::false_type) { return from(std::forward<F>(func)()); }
};
namespace Private
{
//! \private Needed so we can copy forward-declared CVariant.
inline void assign(CVariant &a, const CVariant &b) { a = b; }
}
} // namespace
Q_DECLARE_METATYPE(BlackMisc::CVariant)
namespace BlackMisc
{
namespace Private
{
//! \private Needed so we can copy forward-declared CVariant.
inline void assign(CVariant &a, const CVariant &b) { a = b; }
//! \private
template <typename T, typename>
void maybeRegisterMetaListConvert(int)
{
if (QMetaType::hasRegisteredConverterFunction(qMetaTypeId<T>(), qMetaTypeId<QVector<CVariant>>())) { return; }
QMetaType::registerConverter<T, QVector<CVariant>>([](const T &list) -> QVector<CVariant>
{
return list.transform([](const typename T::value_type &v) { return CVariant::from(v); });
});
QMetaType::registerConverter<QVector<CVariant>, T>([](const QVector<CVariant> &list) -> T
{
return makeRange(list).transform([](const CVariant &v) { return v.to<typename T::value_type>(); });
});
}
}
} // namespace
#endif

View File

@@ -7,9 +7,33 @@
*/
#include "blackmisc/variantlist.h"
#include <algorithm>
#include <iterator>
namespace BlackMisc
{
CVariantList::CVariantList(const CSequence &other) : CSequence(other)
{}
CVariantList::CVariantList(const QVariantList &other)
{
std::copy(other.begin(), other.end(), std::back_inserter(*this));
}
CVariantList::CVariantList(QVariantList &&other)
{
std::move(other.begin(), other.end(), std::back_inserter(*this));
}
CVariantList::CVariantList(const QSequentialIterable &other)
{
for (auto it = other.begin(); it != other.end(); ++it) { push_back(*it); }
}
void CVariantList::registerMetadata()
{
Mixin::MetaType<CVariantList>::registerMetadata();
QMetaType::registerConverter<CVariantList, QVector<CVariant>>([](const CVariantList &list) { return list.toVector(); });
QMetaType::registerConverter<QVector<CVariant>, CVariantList>([](const QVector<CVariant> &list) { return CSequence(list); });
}
} // ns

View File

@@ -15,11 +15,17 @@
#include "blackmisc/collection.h"
#include "blackmisc/sequence.h"
#include "blackmisc/variant.h"
#include <QVariantList>
#include <QMetaType>
#include <iterator>
namespace BlackMisc
{
//! Value object encapsulating a list of variants.
/*!
* Value object encapsulating a list of variants.
*
* A CVariant containing any registered sequential container type can be converted to a CVariantList.
*/
class BLACKMISC_EXPORT CVariantList :
public CSequence<CVariant>,
public BlackMisc::Mixin::MetaType<CVariantList>
@@ -33,6 +39,26 @@ namespace BlackMisc
//! Construct from a base class object.
CVariantList(const CSequence &other);
//! Construct from a QVariantList.
CVariantList(const QVariantList &other);
//! Construct from a moved QVariantList.
CVariantList(QVariantList &&other);
//! Construct from a QSequentialIterable.
CVariantList(const QSequentialIterable &other);
//! Convert to a sequence type by converting all elements.
template <typename T>
T to() const { return CVariant::from(*this).template to<T>(); }
//! Convert from a sequence type by converting all elements.
template <typename T>
static CVariantList from(const T &list) { return CVariant::from(list).template to<CVariantList>(); }
//! \copydoc BlackMisc::CValueObject::registerMetadata
static void registerMetadata();
};
}

View File

@@ -27,6 +27,9 @@ namespace BlackMisc
class CPropertyIndex;
class CIcon;
template <typename T>
class CSequence;
template <typename T>
void registerMetaValueType();
@@ -234,11 +237,16 @@ namespace BlackMisc
IValueObjectMetaInfo *getValueObjectMetaInfo() { return getValueObjectMetaInfo(qMetaTypeId<T>()); }
//! \cond PRIVATE
template <typename T, typename = std::enable_if_t<std::is_base_of<CSequence<typename T::value_type>, T>::value && ! std::is_same<typename T::value_type, CVariant>::value>>
void maybeRegisterMetaListConvert(int);
template <typename T>
void maybeRegisterMetaListConvert(...) {}
template <typename T, bool IsRegisteredMetaType /* = true */>
struct MetaTypeHelperImpl
{
static constexpr int maybeGetMetaTypeId() { return qMetaTypeId<T>(); }
static void maybeRegisterMetaType() { qRegisterMetaType<T>(); qDBusRegisterMetaType<T>(); qRegisterMetaTypeStreamOperators<T>(); registerMetaValueType<T>(); }
static void maybeRegisterMetaType() { qRegisterMetaType<T>(); qDBusRegisterMetaType<T>(); qRegisterMetaTypeStreamOperators<T>(); registerMetaValueType<T>(); maybeRegisterMetaListConvert<T>(0); }
};
template <typename T>

View File

@@ -13,6 +13,7 @@
* \ingroup testblackmisc
*/
#include "blackmisc/aviation/airlineicaocodelist.h"
#include "blackmisc/aviation/atcstation.h"
#include "blackmisc/aviation/callsign.h"
#include "blackmisc/compare.h"
@@ -24,6 +25,7 @@
#include "blackmisc/pq/units.h"
#include "blackmisc/propertyindexvariantmap.h"
#include "blackmisc/registermetadata.h"
#include "blackmisc/variantlist.h"
#include "blackmisc/variant.h"
#include "test.h"
@@ -50,6 +52,9 @@ namespace BlackMiscTest
//! Basic unit tests for value objects and variants
void variant();
//! Unit tests for variant lists
void variantList();
//! Unit tests for value maps and value objects
void valueMap();
};
@@ -106,6 +111,29 @@ namespace BlackMiscTest
QVERIFY2(compare(station1, station3) != 0, "Station should not be equal");
}
void CTestVariantAndMap::variantList()
{
const CSequence<int> ints { 1, 2, 3 };
CVariant variant = CVariant::from(ints);
QVERIFY2(variant.canConvert<CVariantList>(), "Variant containing list can convert to CVariantList");
QVERIFY2(variant.convert(qMetaTypeId<CVariantList>()), "Variant containing list can convert to CVariantList");
const CVariantList variantInts = variant.to<CVariantList>();
QVERIFY2(ints.size() == variantInts.size(), "Variant list has same size as original list");
QVERIFY2(ints[0] == variantInts[0].to<int>(), "Variant list has same element");
QVERIFY2(variant.canConvert<CSequence<int>>(), "Variant containing can convert back");
QVERIFY2(ints == variant.to<CSequence<int>>(), "Variant list converted back compares equal");
const CAirlineIcaoCodeList list { CAirlineIcaoCode("BAW"), CAirlineIcaoCode("DLH"), CAirlineIcaoCode("AAL") };
variant = CVariant::from(list);
QVERIFY2(variant.canConvert<CVariantList>(), "Variant containing list can convert to CVariantList");
QVERIFY2(variant.convert(qMetaTypeId<CVariantList>()), "Variant containing list can convert to CVariantList");
CVariantList variantList = variant.to<CVariantList>();
QVERIFY2(list.size() == variantList.size(), "Variant list has same size as original list");
QVERIFY2(list[0] == variantList[0].to<CAirlineIcaoCode>(), "Variant list has same element");
QVERIFY2(variant.canConvert<CAirlineIcaoCodeList>(), "Variant containing can convert back");
QVERIFY2(list == variant.to<CAirlineIcaoCodeList>(), "Variant list converted back compares equal");
}
void CTestVariantAndMap::valueMap()
{
// ATC station