/* Copyright (C) 2014 VATSIM Community / authors * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef BLACKMISC_TUPLE_PRIVATE_H #define BLACKMISC_TUPLE_PRIVATE_H #include "index_sequence.h" #include #include #include #include #include #include #include #include #include #include #include namespace BlackMisc { class CValueObject; namespace Private { // Inhibit doxygen warnings about missing documentation //! \cond PRIVATE // Using SFINAE to help detect missing BLACK_ENABLE_TUPLE_CONVERSION macro in static_assert std::false_type hasEnabledTupleConversionHelper(...); template typename T::EnabledTupleConversion hasEnabledTupleConversionHelper(T *); template struct HasEnabledTupleConversion { typedef decltype(hasEnabledTupleConversionHelper(static_cast(nullptr))) type; static const bool value = type::value; }; // Using tag dispatch to select which implementation of compare() to use template int compareHelper(const T &a, const T &b, std::true_type isCValueObjectTag) { Q_UNUSED(isCValueObjectTag); return compare(a, b); } template int compareHelper(const T &a, const T &b, std::false_type isCValueObjectTag) { Q_UNUSED(isCValueObjectTag); if (a < b) { return -1; } if (a > b) { return 1; } return 0; } // Create an index_sequence containing indices which match a given predicate. template struct GenSequenceOnPredicate; template struct GenSequenceOnPredicate { static const bool test = P::template test::value; typedef typename GenSequenceOnPredicate::type type; }; template struct GenSequenceOnPredicate { static const bool test = P::template test::value; typedef typename GenSequenceOnPredicate::type type; }; template struct GenSequenceOnPredicate { typedef index_sequence type; }; template struct GenSequenceOnPredicate { typedef index_sequence type; }; template using make_index_sequence_if = typename GenSequenceOnPredicate::value>::type; // Predicates used with make_index_sequence_if. template struct FlagPresent { typedef Tu tuple_type; template struct test : std::integral_constant::type::flags & F)> {}; }; template struct FlagMissing { typedef Tu tuple_type; template struct test : std::integral_constant::type::flags & F)> {}; }; // Combine make_index_sequence_if with predicates to get the indices of tuple elements with certain flags. template auto findFlaggedIndices(Tu &&) -> make_index_sequence_if::type>> { return {}; } template auto skipFlaggedIndices(Tu &&) -> make_index_sequence_if::type>> { return {}; } // A tuple element with attached metadata. template struct Attribute { typedef T type; static const qint64 flags = Flags; Attribute(T &obj, QString jsonName = {}) : m_obj(obj), m_jsonName(jsonName) {} void extend(QString jsonName) { if (m_jsonName.isEmpty()) m_jsonName = jsonName; } T &m_obj; QString m_jsonName; bool operator ==(const Attribute &other) const { return m_obj == other.m_obj; } bool operator !=(const Attribute &other) const { return m_obj != other.m_obj; } bool operator <(const Attribute &other) const { return m_obj < other.m_obj; } bool operator <=(const Attribute &other) const { return m_obj <= other.m_obj; } bool operator >(const Attribute &other) const { return m_obj > other.m_obj; } bool operator >=(const Attribute &other) const { return m_obj >= other.m_obj; } }; // Helpers used in tie(), tieMeta(), and elsewhere, which arrange for the correct types to be passed to std::make_tuple. // See http://en.cppreference.com/w/cpp/utility/tuple/make_tuple // "For each Ti in Types..., the corresponding type Vi in Vtypes... is std::decay::type // unless application of std::decay results in std::reference_wrapper, in which case the deduced type is X&." template std::reference_wrapper tieHelper(T &obj) { return obj; } template std::reference_wrapper tieHelper(Attribute attr) { return attr.m_obj; } template Attribute tieMetaHelper(T &obj) { return obj; } template Attribute tieMetaHelper(Attribute attr) { return attr; } // Compile-time assert for functions which require a meta tuple template struct assertMeta { static_assert(std::is_void::value, "Function expected a meta tuple, got a value tuple"); }; template struct assertMeta...>> {}; // Convert a meta tuple to a value tuple template auto stripMeta(Tu &&tu, index_sequence) -> decltype(std::make_tuple(tieHelper(std::get(std::forward(tu)))...)) { return std::make_tuple(tieHelper(std::get(std::forward(tu)))...); } // Convert a value tuple to a meta tuple with default metadata template auto recoverMeta(Tu &&tu, index_sequence) -> decltype(std::make_tuple(tieMetaHelper(std::get(std::forward(tu)))...)) { return std::make_tuple(tieMetaHelper(std::get(std::forward(tu)))...); } // Fill in any incomplete metadata in a meta tuple, from an appropriate source template void extendMeta(Tu &&tu, const QStringList &jsonNames, index_sequence) { [](...){}((std::get(std::forward(tu)).extend(jsonNames[Is]), 0)...); } // Applying operations to all elements in a tuple, using index_sequence for clean recursion class TupleHelper { public: template static int compare(const Tu &a, const Tu &b, index_sequence) { return compareImpl(std::make_pair(get_ref(a), get_ref(b))...); } template static QDBusArgument &marshall(QDBusArgument &arg, const Tu &tu, index_sequence) { marshallImpl(arg, std::get(tu)...); return arg; } template static const QDBusArgument &unmarshall(const QDBusArgument &arg, Tu &tu, index_sequence) { unmarshallImpl(arg, std::get(tu)...); return arg; } template static QDebug debug(QDebug dbg, const Tu &tu, index_sequence) { debugImpl(dbg, std::get(tu)...); return dbg; } template static uint hash(const Tu &tu, index_sequence) { return hashImpl(qHash(std::get(tu))...); } template static void serializeJson(QJsonObject &json, const QStringList &names, const Tu &tu, index_sequence) { serializeJsonImpl(json, std::make_pair(names[Is], std::get(tu))...); } template static void deserializeJson(const QJsonObject &json, const QStringList &names, Tu &tu, index_sequence) { deserializeJsonImpl(json, std::make_pair(names[Is], get_ref(tu))...); } template static void serializeJson(QJsonObject &json, const Tu &tu, index_sequence) { serializeJsonImpl(json, std::make_pair(std::get(tu).m_jsonName, std::get(tu).m_obj)...); } template static void deserializeJson(const QJsonObject &json, Tu &tu, index_sequence) { deserializeJsonImpl(json, std::make_pair(std::get(tu).m_jsonName, get_ref(tu))...); } private: template static auto get_ref(Tu &&tu) -> decltype(tieHelper(std::get(std::forward(tu)))) { return tieHelper(std::get(std::forward(tu))); } static int compareImpl() { return 0; } template static int compareImpl(const std::pair &head, const Ts &... tail) { int result = compareHelper(head.first, head.second, typename std::is_base_of::type>::type()); if (result) return result; return compareImpl(tail...); } static void marshallImpl(QDBusArgument &) {} template static void marshallImpl(QDBusArgument &arg, const T &head, const Ts &... tail) { arg << head; marshallImpl(arg, tail...); } static void unmarshallImpl(const QDBusArgument &) {} template static void unmarshallImpl(const QDBusArgument &arg, T &head, Ts &... tail) { arg >> head; unmarshallImpl(arg, tail...); } static void debugImpl(QDebug) {} template static void debugImpl(QDebug dbg, const T &head, const Ts &... tail) { dbg << head; debugImpl(dbg, tail...); } static void serializeJsonImpl(QJsonObject &) {} template static void serializeJsonImpl(QJsonObject &json, std::pair head, Ts... tail) { json << head; serializeJsonImpl(json, tail...); } static void deserializeJsonImpl(const QJsonObject &) {} template static void deserializeJsonImpl(const QJsonObject &json, std::pair head, Ts... tail) { json.value(head.first) >> head.second; deserializeJsonImpl(json, tail...); } static uint hashImpl() { return 0; } template static uint hashImpl(uint head, Ts... tail) { return head ^ hashImpl(tail...); } }; //! \endcond } // namespace Private } // namespace BlackMisc #endif // guard