Use if constexpr in metaclass visitor functions

This commit is contained in:
Mat Sutcliffe
2021-04-18 17:50:41 +01:00
parent d7a461ff7a
commit 0ebe21a7cf
7 changed files with 98 additions and 79 deletions

View File

@@ -12,7 +12,6 @@
#define BLACKMISC_METACLASS_H
#include "blackmisc/invoke.h"
#include "blackmisc/integersequence.h"
#include <QHash>
#include <QString>
#include <QLatin1String>
@@ -169,7 +168,7 @@ namespace BlackMisc
//! True if m_flags contains all flags.
template <typename Flags2>
constexpr bool has(Flags2 flags) const { return (MetaFlags<Flags>() & flags) == flags; }
static constexpr bool has(Flags2 flags) { return (MetaFlags<Flags>() & flags) == flags; }
//! Invoke the member on an instance of the value class.
template <typename T, typename... Ts>
@@ -266,34 +265,12 @@ namespace BlackMisc
class CMetaClassIntrospector
{
public:
//! Return a CMetaClassIntrospector<T> covering only those members which have the given flags.
//! \see BlackMisc::MetaFlags
template <typename Flags>
constexpr static auto with(Flags) { return filter(MaskSequence<(members().at(index<Is>()).has(Flags()))...>()); }
//! Return a CMetaClassIntrospector<T> covering only those members which do not have the given flags.
//! \see BlackMisc::MetaFlags
template <typename Flags>
constexpr static auto without(Flags) { return filter(MaskSequence<(! members().at(index<Is>()).has(Flags()))...>()); }
//! For each metamember in metaclass, pass metamember as argument to visitor function.
template <typename F>
static void forEachMember(F &&visitor)
{
(static_cast<void>(std::forward<F>(visitor)(members().at(index<Is>()))), ...);
(static_cast<void>(std::forward<F>(visitor)(MetaClass::getMemberList().at(std::integral_constant<size_t, Is>()))), ...);
}
private:
template <bool... Mask>
using MaskSequence = Private::MaskSequence<std::index_sequence<Is...>, Mask...>;
template <size_t... Js>
constexpr static auto filter(std::index_sequence<Js...>) { return CMetaClassIntrospector<T, MetaClass, Js...>(); }
template <size_t I>
using index = std::integral_constant<size_t, I>;
constexpr static auto members() { return MetaClass::getMemberList(); }
};
// *INDENT-ON*

View File

@@ -58,9 +58,14 @@ namespace BlackMisc
private:
static bool equals(const Derived &a, const Derived &b)
{
constexpr auto meta = introspect<Derived>().without(MetaFlags<DisabledForComparison>());
bool result = baseEquals(static_cast<const TBaseOfT<Derived> *>(&a), static_cast<const TBaseOfT<Derived> *>(&b));
meta.forEachMember([ & ](auto member) { result = result && EqualsByMetaClass::membersEqual(member.in(a), member.in(b), member.m_flags); });
introspect<Derived>().forEachMember([ & ](auto member)
{
if constexpr (!decltype(member)::has(MetaFlags<DisabledForComparison>()))
{
result = result && EqualsByMetaClass::membersEqual(member.in(a), member.in(b), member.m_flags);
}
});
return result;
}
template <typename T> static bool baseEquals(const T *a, const T *b) { return *a == *b; }
@@ -119,10 +124,15 @@ namespace BlackMisc
private:
static bool less(const Derived &a, const Derived &b)
{
constexpr auto meta = introspect<Derived>().without(MetaFlags<DisabledForComparison>());
bool result = baseLess(static_cast<const TBaseOfT<Derived> *>(&a), static_cast<const TBaseOfT<Derived> *>(&b));
bool gt = baseLess(static_cast<const TBaseOfT<Derived> *>(&b), static_cast<const TBaseOfT<Derived> *>(&a));
meta.forEachMember([ & ](auto member) { result = result || LessThanByMetaClass::membersLess(gt, member.in(a), member.in(b), member.m_flags); });
introspect<Derived>().forEachMember([ & ](auto member)
{
if constexpr (!decltype(member)::has(MetaFlags<DisabledForComparison>()))
{
result = result || LessThanByMetaClass::membersLess(gt, member.in(a), member.in(b), member.m_flags);
}
});
return result;
}
template <typename T> static bool baseLess(const T *a, const T *b) { return *a < *b; }
@@ -155,9 +165,14 @@ namespace BlackMisc
private:
static int compareImpl(const Derived &a, const Derived &b)
{
constexpr auto meta = introspect<Derived>().without(MetaFlags<DisabledForComparison>());
int result = baseCompare(static_cast<const TBaseOfT<Derived> *>(&a), static_cast<const TBaseOfT<Derived> *>(&b));
meta.forEachMember([ & ](auto member) { result = result ? result : CompareByMetaClass::membersCompare(member.in(a), member.in(b), member.m_flags); });
introspect<Derived>().forEachMember([ & ](auto member)
{
if constexpr (!decltype(member)::has(MetaFlags<DisabledForComparison>()))
{
result = result ? result : CompareByMetaClass::membersCompare(member.in(a), member.in(b), member.m_flags);
}
});
return result;
}
template <typename T> static int baseCompare(const T *a, const T *b) { return compare(*a, *b); }

View File

@@ -58,16 +58,26 @@ namespace BlackMisc
void marshalToDataStream(QDataStream &stream) const
{
baseMarshal(static_cast<const TBaseOfT<Derived> *>(derived()), stream);
constexpr auto meta = introspect<Derived>().without(MetaFlags<DisabledForMarshalling>());
meta.forEachMember([ &, this ](auto member) { stream << member.in(*this->derived()); });
introspect<Derived>().forEachMember([ &, this ](auto member)
{
if constexpr (!decltype(member)::has(MetaFlags<DisabledForMarshalling>()))
{
stream << member.in(*this->derived());
}
});
}
//! Unmarshal a value from a QDataStream.
void unmarshalFromDataStream(QDataStream &stream)
{
baseUnmarshal(static_cast<TBaseOfT<Derived> *>(derived()), stream);
constexpr auto meta = introspect<Derived>().without(MetaFlags<DisabledForMarshalling>());
meta.forEachMember([ &, this ](auto member) { stream >> member.in(*this->derived()); });
introspect<Derived>().forEachMember([ &, this ](auto member)
{
if constexpr (!decltype(member)::has(MetaFlags<DisabledForMarshalling>()))
{
stream >> member.in(*this->derived());
}
});
}
private:

View File

@@ -71,19 +71,21 @@ namespace BlackMisc
void marshallToDbus(QDBusArgument &arg, Tags...) const
{
baseMarshall(static_cast<const TBaseOfT<Derived> *>(derived()), arg);
constexpr auto meta = introspect<Derived>().without(MetaFlags<DisabledForMarshalling>());
meta.forEachMember([ &, this ](auto member)
introspect<Derived>().forEachMember([ &, this ](auto member)
{
const auto &value = member.in(*this->derived());
if constexpr (THasMarshallMethods<std::decay_t<decltype(value)>>::value)
if constexpr (!decltype(member)::has(MetaFlags<DisabledForMarshalling>()))
{
if constexpr (member.has(MetaFlags<LosslessMarshalling>()))
const auto &value = member.in(*this->derived());
if constexpr (THasMarshallMethods<std::decay_t<decltype(value)>>::value)
{
value.marshallToDbus(arg, LosslessTag());
if constexpr (member.has(MetaFlags<LosslessMarshalling>()))
{
value.marshallToDbus(arg, LosslessTag());
}
else { value.marshallToDbus(arg); }
}
else { value.marshallToDbus(arg); }
else { arg << value; }
}
else { arg << value; }
});
}
@@ -91,19 +93,21 @@ namespace BlackMisc
void unmarshallFromDbus(const QDBusArgument &arg, Tags...)
{
baseUnmarshall(static_cast<TBaseOfT<Derived> *>(derived()), arg);
constexpr auto meta = introspect<Derived>().without(MetaFlags<DisabledForMarshalling>());
meta.forEachMember([ &, this ](auto member)
introspect<Derived>().forEachMember([ &, this ](auto member)
{
auto &value = member.in(*this->derived());
if constexpr (THasMarshallMethods<std::decay_t<decltype(value)>>::value)
if constexpr (!decltype(member)::has(MetaFlags<DisabledForMarshalling>()))
{
if constexpr (member.has(MetaFlags<LosslessMarshalling>()))
auto &value = member.in(*this->derived());
if constexpr (THasMarshallMethods<std::decay_t<decltype(value)>>::value)
{
value.unmarshallFromDbus(arg, LosslessTag());
if constexpr (member.has(MetaFlags<LosslessMarshalling>()))
{
value.unmarshallFromDbus(arg, LosslessTag());
}
else { value.unmarshallFromDbus(arg); }
}
else { value.unmarshallFromDbus(arg); }
else { arg >> value; }
}
else { arg >> value; }
});
}

View File

@@ -60,8 +60,13 @@ namespace BlackMisc
static uint hashImpl(const Derived &value)
{
uint hash = baseHash(static_cast<const TBaseOfT<Derived> *>(&value));
constexpr auto meta = introspect<Derived>().without(MetaFlags<DisabledForHashing>());
meta.forEachMember([ & ](const auto &member) { hash ^= qHash(member.in(value)); });
introspect<Derived>().forEachMember([ & ](auto member)
{
if constexpr (!decltype(member)::has(MetaFlags<DisabledForHashing>()))
{
hash ^= qHash(member.in(value));
}
});
return hash;
}

View File

@@ -96,10 +96,12 @@ namespace BlackMisc
QJsonObject toJson() const
{
QJsonObject json;
constexpr auto meta = introspect<Derived>().without(MetaFlags<DisabledForJson>());
meta.forEachMember([ &, this ](auto member)
introspect<Derived>().forEachMember([ &, this ](auto member)
{
json << std::make_pair(CExplicitLatin1String(member.latin1Name()), std::cref(member.in(*this->derived())));
if constexpr (!decltype(member)::has(MetaFlags<DisabledForJson>()))
{
json << std::make_pair(CExplicitLatin1String(member.latin1Name()), std::cref(member.in(*this->derived())));
}
});
return Json::appendJsonObject(json, baseToJson(static_cast<const TBaseOfT<Derived> *>(derived())));
}
@@ -115,23 +117,25 @@ namespace BlackMisc
void convertFromJson(const QJsonObject &json)
{
baseConvertFromJson(static_cast<TBaseOfT<Derived> *>(derived()), json);
constexpr auto meta = introspect<Derived>().without(MetaFlags<DisabledForJson>());
meta.forEachMember([ &, this ](auto member)
introspect<Derived>().forEachMember([ &, this ](auto member)
{
const auto value = json.value(CExplicitLatin1String(member.latin1Name()));
if (value.isUndefined())
if constexpr (!decltype(member)::has(MetaFlags<DisabledForJson>()))
{
constexpr bool required = false; //! \fixme add RequiredForJson flag in metaclass system
// cppcheck-suppress knownConditionTrueFalse
// QLatin1String used instead of QStringLiteral below since the latter causes an internal compiler bug
// in GCC 8 and higher
if (required) { throw CJsonException(QLatin1String("Missing required member '%1'").arg(member.latin1Name())); }
}
else
{
CJsonScope scope(member.latin1Name());
Q_UNUSED(scope);
value >> member.in(*this->derived());
const auto value = json.value(CExplicitLatin1String(member.latin1Name()));
if (value.isUndefined())
{
constexpr bool required = false; //! \fixme add RequiredForJson flag in metaclass system
// cppcheck-suppress knownConditionTrueFalse
// QLatin1String used instead of QStringLiteral below since the latter causes an internal compiler bug
// in GCC 8 and higher
if (required) { throw CJsonException(QLatin1String("Missing required member '%1'").arg(member.latin1Name())); }
}
else
{
CJsonScope scope(member.latin1Name());
Q_UNUSED(scope);
value >> member.in(*this->derived());
}
}
});
}

View File

@@ -125,22 +125,26 @@ namespace BlackMisc
QJsonObject CAircraftModel::toMemoizedJson(MemoHelper::CMemoizer &helper) const
{
QJsonObject json;
constexpr auto meta = introspect<CAircraftModel>().without(MetaFlags<DisabledForJson>());
meta.forEachMember([ &, this ](auto member)
introspect<CAircraftModel>().forEachMember([ &, this ](auto member)
{
auto &&maybeMemo = helper.maybeMemoize(member.in(*this));
json << std::make_pair(CExplicitLatin1String(member.latin1Name()), std::cref(maybeMemo));
if constexpr (!decltype(member)::has(MetaFlags<DisabledForJson>()))
{
auto &&maybeMemo = helper.maybeMemoize(member.in(*this));
json << std::make_pair(CExplicitLatin1String(member.latin1Name()), std::cref(maybeMemo));
}
});
return json;
}
void CAircraftModel::convertFromMemoizedJson(const QJsonObject &json, const MemoHelper::CUnmemoizer &helper)
{
constexpr auto meta = introspect<CAircraftModel>().without(MetaFlags<DisabledForJson>());
meta.forEachMember([ &, this ](auto member)
introspect<CAircraftModel>().forEachMember([ &, this ](auto member)
{
auto it = json.find(CExplicitLatin1String(member.latin1Name()));
if (it != json.end()) { it.value() >> helper.maybeUnmemoize(member.in(*this)).get(); }
if constexpr (!decltype(member)::has(MetaFlags<DisabledForJson>()))
{
auto it = json.find(CExplicitLatin1String(member.latin1Name()));
if (it != json.end()) { it.value() >> helper.maybeUnmemoize(member.in(*this)).get(); }
}
});
}