/* Copyright (C) 2013 * swift project Community / Contributors * * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, * including this file, may be copied, modified, propagated, or distributed except according to the terms * contained in the LICENSE file. */ #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 CEmpty; template class TupleConverter; template class CValueObject; namespace Private { // Inhibit doxygen warnings about missing documentation //! \cond PRIVATE // To allow CValueObject policy classes to use the tuple system class EncapsulationBreaker { protected: template static auto toMetaTuple(T &o) -> decltype(TupleConverter::type>::toMetaTuple(o)) { return TupleConverter::type>::toMetaTuple(o); } }; // 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 {}; } // CRTP base class for Attribute, to select appropriate method of comparison. template struct AttributeComparable; template struct AttributeComparable { friend int compare(const Derived &, const Derived &) { return 0; } friend bool operator ==(const Derived &, const Derived &) { return true; } friend bool operator !=(const Derived &, const Derived &) { return false; } friend bool operator <(const Derived &, const Derived &) { return false; } friend bool operator <=(const Derived &, const Derived &) { return true; } friend bool operator >(const Derived &, const Derived &) { return false; } friend bool operator >=(const Derived &, const Derived &) { return true; } }; template struct AttributeComparable { template using isCValueObject = typename std::is_base_of::type; // FIXME use TemplateIsBaseOf friend int compare(const Derived &a, const Derived &b) { return compareHelper(a.m_obj, b.m_obj, isCValueObject()); } 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; } 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; } 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; } }; template struct AttributeComparable { friend int compare(const Derived &a, const Derived &b) { return a.m_obj.compare(b.m_obj, Qt::CaseInsensitive); } friend bool operator ==(const Derived &a, const Derived &b) { return a.m_obj.compare(b.m_obj, Qt::CaseInsensitive) == 0; } friend bool operator !=(const Derived &a, const Derived &b) { return a.m_obj.compare(b.m_obj, Qt::CaseInsensitive) != 0; } friend bool operator <(const Derived &a, const Derived &b) { return a.m_obj.compare(b.m_obj, Qt::CaseInsensitive) < 0; } friend bool operator <=(const Derived &a, const Derived &b) { return a.m_obj.compare(b.m_obj, Qt::CaseInsensitive) <= 0; } friend bool operator >(const Derived &a, const Derived &b) { return a.m_obj.compare(b.m_obj, Qt::CaseInsensitive) > 0; } friend bool operator >=(const Derived &a, const Derived &b) { return a.m_obj.compare(b.m_obj, Qt::CaseInsensitive) >= 0; } }; // A tuple element with attached metadata. template struct Attribute : public AttributeComparable, bool(Flags & DisabledForComparison), bool(Flags & CaseInsensitiveComparison)> { 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; }; // 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 void assertMeta(const Tu &) { static_assert(std::is_void::value, "Function expected a meta tuple, got a value tuple"); } template void assertMeta(const std::tuple...> &) {} // 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) // underscore to avoid hiding the name "compare" in other scopes { return compareImpl(std::make_pair(std::get(a), std::get(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], get_ref(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, get_ref(tu))...); Q_UNUSED(tu); // avoid compiler warning when all attributes are JSON disabled } template static void deserializeJson(const QJsonObject &json, Tu &tu, index_sequence) { deserializeJsonImpl(json, std::make_pair(std::get(tu).m_jsonName, get_ref(tu))...); Q_UNUSED(tu); // avoid compiler warning when all attributes are JSON disabled } 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 = compare(head.first, head.second); if (result) return result; return compareImpl(tail...); } static void marshallImpl(QDBusArgument &) {} template static void marshallImpl(QDBusArgument &arg, const T &head, const Ts &... tail) { marshallHelper(arg, head, 0); marshallImpl(arg, tail...); } template ::value, int>::type = 0> static void marshallHelper(QDBusArgument &arg, const T &val, int) { static_cast(val).marshallToDbus(arg); } template static void marshallHelper(QDBusArgument &arg, const T &val, ...) { arg << val; } static void unmarshallImpl(const QDBusArgument &) {} template static void unmarshallImpl(const QDBusArgument &arg, T &head, Ts &... tail) { unmarshallHelper(arg, head, 0); unmarshallImpl(arg, tail...); } template ::value, int>::type = 0> static void unmarshallHelper(const QDBusArgument &arg, T &val, int) { static_cast(val).unmarshallFromDbus(arg); } template static void unmarshallHelper(const QDBusArgument &arg, T &val, ...) { arg >> val; } 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