From fcd3dc09efccffc9b1d8b72e134961cfb05b6ae4 Mon Sep 17 00:00:00 2001 From: Mathew Sutcliffe Date: Wed, 12 Feb 2014 23:00:46 +0000 Subject: [PATCH] refs #140 framework for using std::tuple to reduce repetitive coding in value classes --- src/blackmisc/blackmisc.h | 5 + src/blackmisc/tuple.h | 494 ++++++++++++++++++++++++++++++++++ src/blackmisc/tuple_private.h | 126 +++++++++ src/blackmisc/valueobject.h | 1 + 4 files changed, 626 insertions(+) create mode 100644 src/blackmisc/tuple.h create mode 100644 src/blackmisc/tuple_private.h diff --git a/src/blackmisc/blackmisc.h b/src/blackmisc/blackmisc.h index 82e16f1bd..72bc34196 100644 --- a/src/blackmisc/blackmisc.h +++ b/src/blackmisc/blackmisc.h @@ -54,6 +54,11 @@ * \private */ +/*! + * \namespace BlackMisc::Private + * \private + */ + /*! * \namespace BlackMisc::Iterators * \brief Iterator classes for the containers. diff --git a/src/blackmisc/tuple.h b/src/blackmisc/tuple.h new file mode 100644 index 000000000..de12b77be --- /dev/null +++ b/src/blackmisc/tuple.h @@ -0,0 +1,494 @@ +/* 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/. */ + +/*! + \file +*/ + +#ifndef BLACKMISC_TUPLE_H +#define BLACKMISC_TUPLE_H + +#include "tuple_private.h" + +/*! + * \brief Macro to make a class available to TupleConverter. + * \details Put this macro anywhere in a class declaration to make it usable in TupleConverter. + * \param T The name of the class. + * \hideinitializer + */ +#define BLACK_ENABLE_TUPLE_CONVERSION(T) \ + public: typedef std::true_type EnabledTupleConversion; \ + private: friend class BlackMisc::TupleConverter; + +/*! + * \brief Macro to make a class known to TupleConverter. + * \details Put this macro outside of any namespace, in the same header as T, to make it usable in TupleConverter. + * \param T The fully qualified name of the class. + * \param MEMBERS A parenthesized, comma-separated list of the data members of T, with each member prefixed by "o." + * (the letter o followed by dot). + * \par Example + * If class Things::MyThing has data members m_first, m_second, and m_third: + * \code + * BLACK_DEFINE_TUPLE_CONVERSION(Things::MyThing, (o.m_first, o.m_second, o.m_third)) + * \endcode + * \see BLACK_DECLARE_TUPLE_CONVERSION_TEMPLATE If T is a template, use this instead. + * \hideinitializer + */ +#define BLACK_DECLARE_TUPLE_CONVERSION(T, MEMBERS) \ + namespace BlackMisc \ + { \ + template <> class TupleConverter \ + { \ + friend class T; \ + static_assert(Private::HasEnabledTupleConversion::type::value, \ + "Missing BLACK_ENABLE_TUPLE_CONVERSION macro in " #T); \ + static auto toTuple(const T &o) -> decltype(std::tie MEMBERS) \ + { \ + return std::tie MEMBERS; \ + } \ + static auto toTuple(T &o) -> decltype(std::tie MEMBERS) \ + { \ + return std::tie MEMBERS; \ + } \ + }; \ + } + +/*! + * \brief Same as BLACK_DECLARE_TUPLE_CONVERSION(), but T can be a class template. + * \hideinitializer + */ +#define BLACK_DECLARE_TUPLE_CONVERSION_TEMPLATE(T, MEMBERS) \ + namespace BlackMisc \ + { \ + template class TupleConverter> \ + { \ + friend class T; \ + static_assert(Private::HasEnabledTupleConversion>::type::value,\ + "Missing BLACK_ENABLE_TUPLE_CONVERSION macro in " #T); \ + static auto toTuple(const T &o) -> decltype(std::tie MEMBERS) \ + { \ + return std::tie MEMBERS; \ + } \ + static auto toTuple(T &o) -> decltype(std::tie MEMBERS) \ + { \ + return std::tie MEMBERS; \ + } \ + }; \ + } + +namespace BlackMisc +{ + + /*! + * \brief Class template for converting class objects to tuples + * \details If a class T uses the BLACK_ENABLE_TUPLE_CONVERSION() and BLACK_DECLARE_TUPLE_CONVERSION() macros, and object + * is an instance of T, then TupleConverter::toTuple(object) will return a std::tuple representing object. + * \remarks The toTuple() function is a private member of TupleConverter, and the class T is declared to be a friend. + * \nosubgrouping + */ + template class TupleConverter + { + public: + //! \name Static Private Member Functions + //! @{ + /*! + * \brief Returns a tuple of references to object's data members as they were declared in BLACK_DECLARE_TUPLE_CONVERSION(). + * Can be used like std::tie . + */ + static std::tuple<> toTuple(const T &object) + { + // BLACK_DECLARE_TUPLE_CONVERSION creates an explicit specialization of TupleConverter, + // so this unspecialized template will only be used if the macro is missing. + + static_assert(std::is_void::value, // always false; is_void<> trick is just to make the condition dependent on the template parameter T + "Missing BLACK_DECLARE_TUPLE_CONVERSION macro for T"); + + Q_UNUSED(object); + return std::tuple<>(); // suppress "no return statement" warning + } + //! @} + }; + + // Needed so that our qHash overload doesn't hide the qHash overloads in the global namespace. + // This will be safe as long as no global qHash has the same signature as ours. + // Alternative would be to qualify all our invokations of the global qHash as ::qHash. + using ::qHash; + +#ifdef Q_COMPILER_VARIADIC_TEMPLATES + + /*! + * \brief Lexicographical tuple comparison function which is CValueObject-aware. + * \details Tuple members which are CValueObjects are compared using the compare() friend function of CValueObject; + * other tuple members are compared using operator< and operator>. + */ + template + int compare(std::tuple a, std::tuple b) + { + return Private::TupleHelper::compare(a, b); + } + + /*! + * \brief Marshall the elements of a tuple into a QDBusArgument. + */ + template + QDBusArgument &operator <<(QDBusArgument &arg, std::tuple tu) + { + return Private::TupleHelper::marshall(arg, tu); + } + + /*! + * \brief Demarshall a QDBusArgument into the elements of a tuple. + */ + template + const QDBusArgument &operator >>(const QDBusArgument &arg, std::tuple tu) + { + return Private::TupleHelper::unmarshall(arg, tu); + } + + /*! + * \brief Generate a hash value from the elements of a tuple. + */ + template + uint qHash(std::tuple tu) + { + return Private::TupleHelper::hash(tu); + } + +#else // !Q_COMPILER_VARIADIC_TEMPLATES + + inline int compare(std::tuple<>, std::tuple<>) + { + return 0; + } + + template + int compare(std::tuple a, std::tuple b) + { + return Private::compareHelper<0>(a, b); + } + + template + int compare(std::tuple a, std::tuple b) + { + int result; + if ((result = Private::compareHelper<0>(a, b))) { return result; } + return Private::compareHelper<1>(a, b); + } + + template + int compare(std::tuple a, std::tuple b) + { + int result; + if ((result = Private::compareHelper<0>(a, b))) { return result; } + if ((result = Private::compareHelper<1>(a, b))) { return result; } + return Private::compareHelper<2>(a, b); + } + + template + int compare(std::tuple a, std::tuple b) + { + int result; + if ((result = Private::compareHelper<0>(a, b))) { return result; } + if ((result = Private::compareHelper<1>(a, b))) { return result; } + if ((result = Private::compareHelper<2>(a, b))) { return result; } + return Private::compareHelper<3>(a, b); + } + + template + int compare(std::tuple a, std::tuple b) + { + int result; + if ((result = Private::compareHelper<0>(a, b))) { return result; } + if ((result = Private::compareHelper<1>(a, b))) { return result; } + if ((result = Private::compareHelper<2>(a, b))) { return result; } + if ((result = Private::compareHelper<3>(a, b))) { return result; } + return Private::compareHelper<4>(a, b); + } + + template + int compare(std::tuple a, std::tuple b) + { + int result; + if ((result = Private::compareHelper<0>(a, b))) { return result; } + if ((result = Private::compareHelper<1>(a, b))) { return result; } + if ((result = Private::compareHelper<2>(a, b))) { return result; } + if ((result = Private::compareHelper<3>(a, b))) { return result; } + if ((result = Private::compareHelper<4>(a, b))) { return result; } + return Private::compareHelper<5>(a, b); + } + + template + int compare(std::tuple a, std::tuple b) + { + int result; + if ((result = Private::compareHelper<0>(a, b))) { return result; } + if ((result = Private::compareHelper<1>(a, b))) { return result; } + if ((result = Private::compareHelper<2>(a, b))) { return result; } + if ((result = Private::compareHelper<3>(a, b))) { return result; } + if ((result = Private::compareHelper<4>(a, b))) { return result; } + if ((result = Private::compareHelper<5>(a, b))) { return result; } + return Private::compareHelper<6>(a, b); + } + + template + int compare(std::tuple a, std::tuple b) + { + int result; + if ((result = Private::compareHelper<0>(a, b))) { return result; } + if ((result = Private::compareHelper<1>(a, b))) { return result; } + if ((result = Private::compareHelper<2>(a, b))) { return result; } + if ((result = Private::compareHelper<3>(a, b))) { return result; } + if ((result = Private::compareHelper<4>(a, b))) { return result; } + if ((result = Private::compareHelper<5>(a, b))) { return result; } + if ((result = Private::compareHelper<6>(a, b))) { return result; } + return Private::compareHelper<7>(a, b); + } + + template + int compare(std::tuple a, std::tuple b) + { + int result; + if ((result = Private::compareHelper<0>(a, b))) { return result; } + if ((result = Private::compareHelper<1>(a, b))) { return result; } + if ((result = Private::compareHelper<2>(a, b))) { return result; } + if ((result = Private::compareHelper<3>(a, b))) { return result; } + if ((result = Private::compareHelper<4>(a, b))) { return result; } + if ((result = Private::compareHelper<5>(a, b))) { return result; } + if ((result = Private::compareHelper<6>(a, b))) { return result; } + if ((result = Private::compareHelper<7>(a, b))) { return result; } + return Private::compareHelper<8>(a, b); + } + + template + int compare(std::tuple a, std::tuple b) + { + int result; + if ((result = Private::compareHelper<0>(a, b))) { return result; } + if ((result = Private::compareHelper<1>(a, b))) { return result; } + if ((result = Private::compareHelper<2>(a, b))) { return result; } + if ((result = Private::compareHelper<3>(a, b))) { return result; } + if ((result = Private::compareHelper<4>(a, b))) { return result; } + if ((result = Private::compareHelper<5>(a, b))) { return result; } + if ((result = Private::compareHelper<6>(a, b))) { return result; } + if ((result = Private::compareHelper<7>(a, b))) { return result; } + if ((result = Private::compareHelper<8>(a, b))) { return result; } + return Private::compareHelper<9>(a, b); + } + + inline QDBusArgument &operator <<(QDBusArgument &arg, std::tuple<>) + { + return arg; + } + + template + QDBusArgument &operator <<(QDBusArgument &arg, std::tuple tu) + { + return arg << std::get<0>(tu); + } + + template + QDBusArgument &operator <<(QDBusArgument &arg, std::tuple tu) + { + return arg << std::get<0>(tu) << std::get<1>(tu); + } + + template + QDBusArgument &operator <<(QDBusArgument &arg, std::tuple tu) + { + return arg << std::get<0>(tu) << std::get<1>(tu) << std::get<2>(tu); + } + + template + QDBusArgument &operator <<(QDBusArgument &arg, std::tuple tu) + { + return arg << std::get<0>(tu) << std::get<1>(tu) << std::get<2>(tu) << std::get<3>(tu); + } + + template + QDBusArgument &operator <<(QDBusArgument &arg, std::tuple tu) + { + return arg << std::get<0>(tu) << std::get<1>(tu) << std::get<2>(tu) << std::get<3>(tu) << std::get<4>(tu); + } + + template + QDBusArgument &operator <<(QDBusArgument &arg, std::tuple tu) + { + return arg << std::get<0>(tu) << std::get<1>(tu) << std::get<2>(tu) << std::get<3>(tu) << std::get<4>(tu) + << std::get<5>(tu); + } + + template + QDBusArgument &operator <<(QDBusArgument &arg, std::tuple tu) + { + return arg << std::get<0>(tu) << std::get<1>(tu) << std::get<2>(tu) << std::get<3>(tu) << std::get<4>(tu) + << std::get<5>(tu) << std::get<6>(tu); + } + + template + QDBusArgument &operator <<(QDBusArgument &arg, std::tuple tu) + { + return arg << std::get<0>(tu) << std::get<1>(tu) << std::get<2>(tu) << std::get<3>(tu) << std::get<4>(tu) + << std::get<5>(tu) << std::get<6>(tu) << std::get<7>(tu); + } + + template + QDBusArgument &operator <<(QDBusArgument &arg, std::tuple tu) + { + return arg << std::get<0>(tu) << std::get<1>(tu) << std::get<2>(tu) << std::get<3>(tu) << std::get<4>(tu) + << std::get<5>(tu) << std::get<6>(tu) << std::get<7>(tu) << std::get<8>(tu); + } + + template + QDBusArgument &operator <<(QDBusArgument &arg, std::tuple tu) + { + return arg << std::get<0>(tu) << std::get<1>(tu) << std::get<2>(tu) << std::get<3>(tu) << std::get<4>(tu) + << std::get<5>(tu) << std::get<6>(tu) << std::get<7>(tu) << std::get<8>(tu) << std::get<9>(tu); + } + + inline const QDBusArgument &operator >>(const QDBusArgument &arg, std::tuple<>) + { + return arg; + } + + template + const QDBusArgument &operator >>(const QDBusArgument &arg, std::tuple tu) + { + return arg >> std::get<0>(tu); + } + + template + const QDBusArgument &operator >>(const QDBusArgument &arg, std::tuple tu) + { + return arg >> std::get<0>(tu) >> std::get<1>(tu); + } + + template + const QDBusArgument &operator >>(const QDBusArgument &arg, std::tuple tu) + { + return arg >> std::get<0>(tu) >> std::get<1>(tu) >> std::get<2>(tu); + } + + template + const QDBusArgument &operator >>(const QDBusArgument &arg, std::tuple tu) + { + return arg >> std::get<0>(tu) >> std::get<1>(tu) >> std::get<2>(tu) >> std::get<3>(tu); + } + + template + const QDBusArgument &operator >>(const QDBusArgument &arg, std::tuple tu) + { + return arg >> std::get<0>(tu) >> std::get<1>(tu) >> std::get<2>(tu) >> std::get<3>(tu) >> std::get<4>(tu); + } + + template + const QDBusArgument &operator >>(const QDBusArgument &arg, std::tuple tu) + { + return arg >> std::get<0>(tu) >> std::get<1>(tu) >> std::get<2>(tu) >> std::get<3>(tu) >> std::get<4>(tu) + >> std::get<5>(tu); + } + + template + const QDBusArgument &operator >>(const QDBusArgument &arg, std::tuple tu) + { + return arg >> std::get<0>(tu) >> std::get<1>(tu) >> std::get<2>(tu) >> std::get<3>(tu) >> std::get<4>(tu) + >> std::get<5>(tu) >> std::get<6>(tu); + } + + template + const QDBusArgument &operator >>(const QDBusArgument &arg, std::tuple tu) + { + return arg >> std::get<0>(tu) >> std::get<1>(tu) >> std::get<2>(tu) >> std::get<3>(tu) >> std::get<4>(tu) + >> std::get<5>(tu) >> std::get<6>(tu) >> std::get<7>(tu); + } + + template + const QDBusArgument &operator >>(const QDBusArgument &arg, std::tuple tu) + { + return arg >> std::get<0>(tu) >> std::get<1>(tu) >> std::get<2>(tu) >> std::get<3>(tu) >> std::get<4>(tu) + >> std::get<5>(tu) >> std::get<6>(tu) >> std::get<7>(tu) >> std::get<8>(tu); + } + + template + const QDBusArgument &operator >>(const QDBusArgument &arg, std::tuple tu) + { + return arg >> std::get<0>(tu) >> std::get<1>(tu) >> std::get<2>(tu) >> std::get<3>(tu) >> std::get<4>(tu) + >> std::get<5>(tu) >> std::get<6>(tu) >> std::get<7>(tu) >> std::get<8>(tu) >> std::get<9>(tu); + } + + inline uint qHash(std::tuple<>) + { + return 0; + } + + template + uint qHash(std::tuple tu) + { + return qHash(std::get<0>(tu)); + } + + template + uint qHash(std::tuple tu) + { + return qHash(std::get<0>(tu)) ^ qHash(std::get<1>(tu)); + } + + template + uint qHash(std::tuple tu) + { + return qHash(std::get<0>(tu)) ^ qHash(std::get<1>(tu)) ^ qHash(std::get<2>(tu)); + } + + template + uint qHash(std::tuple tu) + { + return qHash(std::get<0>(tu)) ^ qHash(std::get<1>(tu)) ^ qHash(std::get<2>(tu)) ^ qHash(std::get<3>(tu)); + } + + template + uint qHash(std::tuple tu) + { + return qHash(std::get<0>(tu)) ^ qHash(std::get<1>(tu)) ^ qHash(std::get<2>(tu)) ^ qHash(std::get<3>(tu)) ^ qHash(std::get<4>(tu)); + } + + template + uint qHash(std::tuple tu) + { + return qHash(std::get<0>(tu)) ^ qHash(std::get<1>(tu)) ^ qHash(std::get<2>(tu)) ^ qHash(std::get<3>(tu)) ^ qHash(std::get<4>(tu)) + ^ qHash(std::get<5>(tu)); + } + + template + uint qHash(std::tuple tu) + { + return qHash(std::get<0>(tu)) ^ qHash(std::get<1>(tu)) ^ qHash(std::get<2>(tu)) ^ qHash(std::get<3>(tu)) ^ qHash(std::get<4>(tu)) + ^ qHash(std::get<5>(tu)) ^ qHash(std::get<6>(tu)); + } + + template + uint qHash(std::tuple tu) + { + return qHash(std::get<0>(tu)) ^ qHash(std::get<1>(tu)) ^ qHash(std::get<2>(tu)) ^ qHash(std::get<3>(tu)) ^ qHash(std::get<4>(tu)) + ^ qHash(std::get<5>(tu)) ^ qHash(std::get<6>(tu)) ^ qHash(std::get<7>(tu)); + } + + template + uint qHash(std::tuple tu) + { + return qHash(std::get<0>(tu)) ^ qHash(std::get<1>(tu)) ^ qHash(std::get<2>(tu)) ^ qHash(std::get<3>(tu)) ^ qHash(std::get<4>(tu)) + ^ qHash(std::get<5>(tu)) ^ qHash(std::get<6>(tu)) ^ qHash(std::get<7>(tu)) ^ qHash(std::get<8>(tu)); + } + + template + uint qHash(std::tuple tu) + { + return qHash(std::get<0>(tu)) ^ qHash(std::get<1>(tu)) ^ qHash(std::get<2>(tu)) ^ qHash(std::get<3>(tu)) ^ qHash(std::get<4>(tu)) + ^ qHash(std::get<5>(tu)) ^ qHash(std::get<6>(tu)) ^ qHash(std::get<7>(tu)) ^ qHash(std::get<8>(tu)) ^ qHash(std::get<9>(tu)); + } + +#endif // Q_COMPILER_VARIADIC_TEMPLATES + +} // namespace BlackMisc + +#endif // guard diff --git a/src/blackmisc/tuple_private.h b/src/blackmisc/tuple_private.h new file mode 100644 index 000000000..3f50369b8 --- /dev/null +++ b/src/blackmisc/tuple_private.h @@ -0,0 +1,126 @@ +/* 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/. */ + +/*! + \file + Private implementation details used by tuple.h +*/ + +#ifndef BLACKMISC_TUPLE_PRIVATE_H +#define BLACKMISC_TUPLE_PRIVATE_H + +#include +#include +#include +#include + +namespace BlackMisc +{ + + class CValueObject; + + namespace Private + { + + //! \private + template + typename T::EnabledTupleConversion hasEnabledTupleConversionHelper(T *); + + //! \private + std::false_type hasEnabledTupleConversionHelper(...); + + //! \private + template + struct HasEnabledTupleConversion + { + typedef decltype(hasEnabledTupleConversionHelper(static_cast(nullptr))) type; + }; + + //! \private + template + int compareHelper(const T &a, const T &b, std::true_type isCValueObject) + { + Q_UNUSED(isCValueObject); + return compare(a, b); + } + + //! \private + template + int compareHelper(const T &a, const T &b, std::false_type isCValueObject) + { + Q_UNUSED(isCValueObject); + if (a < b) { return -1; } + if (a > b) { return 1; } + return 0; + } + + //! \private + template + int compareHelper(const Tu &a, const Tu &b) + { + typedef typename std::is_base_of< + CValueObject, + typename std::decay< + typename std::tuple_element::type + >::type + >::type isCValueObject; + + return compareHelper(std::get(a), std::get(b), isCValueObject()); + } + +#ifdef Q_COMPILER_VARIADIC_TEMPLATES + + //! \private + template + struct TupleHelper + { + template + static int compare(const Tu &a, const Tu &b) + { + const int head = TupleHelper::compare(a, b); + if (head) { return head; } + return compareHelper(a, b); + } + + template + static QDBusArgument &marshall(QDBusArgument &arg, const Tu &tu) + { + return TupleHelper::marshall(arg, tu) << std::get(tu); + } + + template + static const QDBusArgument &unmarshall(const QDBusArgument &arg, Tu &tu) + { + return TupleHelper::unmarshall(arg, tu) >> std::get(tu); + } + + template + static uint hash(const Tu &tu) + { + return TupleHelper::hash(tu) ^ qHash(std::get(tu)); + } + }; + + //! \private + template <> + struct TupleHelper<0> + { + template + static int compare(const Tu &, const Tu &) { return 0; } + template + static QDBusArgument &marshall(QDBusArgument &arg, const Tu &) { return arg; } + template + static const QDBusArgument &unmarshall(const QDBusArgument &arg, Tu &) { return arg; } + template + static uint hash(const Tu &) { return 0; } + }; + +#endif // Q_COMPILER_VARIADIC_TEMPLATES + + } // namespace Private + +} // namespace BlackMisc + +#endif // guard diff --git a/src/blackmisc/valueobject.h b/src/blackmisc/valueobject.h index bd2910609..fd79944ab 100644 --- a/src/blackmisc/valueobject.h +++ b/src/blackmisc/valueobject.h @@ -1,6 +1,7 @@ #ifndef BLACKMISC_VALUEOBJECT_H #define BLACKMISC_VALUEOBJECT_H +#include "tuple.h" #include #include #include