diff --git a/src/blackmisc/tuple.cpp b/src/blackmisc/tuple.cpp index bb627024a..4760379d6 100644 --- a/src/blackmisc/tuple.cpp +++ b/src/blackmisc/tuple.cpp @@ -27,6 +27,8 @@ namespace BlackMisc { QRegExp simple("^o\\.(\\w+)$"); if (member.contains(simple)) { m_names.push_back(simple.cap(1)); continue; } + QRegExp meta("^attr\\s*\\(\\s*o\\.(\\w+)"); + if (member.contains(meta)) { m_names.push_back(meta.cap(1)); continue; } qFatal("BLACK_DECLARE_TUPLE_CONVERSION: Parser couldn't extract member name from \"%s\"", qPrintable(member)); } for (auto &name : m_names) { name.remove(QRegExp("^m_")); } diff --git a/src/blackmisc/tuple.h b/src/blackmisc/tuple.h index 395bcb040..f051f88b6 100644 --- a/src/blackmisc/tuple.h +++ b/src/blackmisc/tuple.h @@ -30,12 +30,16 @@ * \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). + * (the letter o followed by dot). Can also use any types or functions inherited from TupleConverterBase. * \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 + * To disable m_third from participating in hash value generation: + * \code + * BLACK_DEFINE_TUPLE_CONVERSION(Things::MyThing, (o.m_first, o.m_second, attr(o.m_third, flags()))) + * \endcode * \see BLACK_DECLARE_TUPLE_CONVERSION_TEMPLATE If T is a template, use this instead. * \hideinitializer * \ingroup Tuples @@ -56,6 +60,16 @@ { \ return BlackMisc::tie MEMBERS; \ } \ + static auto toMetaTuple(const T &o) -> decltype(BlackMisc::tieMeta MEMBERS) \ + { \ + auto tu = BlackMisc::tieMeta MEMBERS; \ + return tu; \ + } \ + static auto toMetaTuple(T &o) -> decltype(BlackMisc::tieMeta MEMBERS) \ + { \ + auto tu = BlackMisc::tieMeta MEMBERS; \ + return tu; \ + } \ static const Parser &parser() \ { \ static const Parser p(#MEMBERS); \ @@ -94,6 +108,16 @@ { \ return BlackMisc::tie MEMBERS; \ } \ + static auto toMetaTuple(const T &o) -> decltype(BlackMisc::tieMeta MEMBERS) \ + { \ + auto tu = BlackMisc::tieMeta MEMBERS; \ + return tu; \ + } \ + static auto toMetaTuple(T &o) -> decltype(BlackMisc::tieMeta MEMBERS) \ + { \ + auto tu = BlackMisc::tieMeta MEMBERS; \ + return tu; \ + } \ static const Parser &parser() \ { \ static const Parser p(#MEMBERS); \ @@ -121,7 +145,36 @@ namespace BlackMisc */ class TupleConverterBase { + public: + //! \brief Metadata flags attached to tuple elements. + enum Flags + { + DisabledForComparison = 1 << 0, //!< Element will be ignored by compare(), but not by operators + DisabledForMarshalling = 1 << 1, //!< Element will be ignored during DBus marshalling + DisabledForDebugging = 1 << 2, //!< Element will be ignored when streaming to QDebug + DisabledForHashing = 1 << 3, //!< Element will be ignored by qHash() + DisabledForJson = 1 << 4 //!< Element will be ignored during JSON serialization + }; + protected: + //! \brief A shorthand alias for passing flags as a compile-time constant. + template + using flags = std::integral_constant; + + //! \brief Create a tuple element with default metadata. + template + static Private::Attribute attr(T &obj) + { + return { obj }; + } + + //! \brief Create a tuple element with attached metadata. + template + static Private::Attribute attr(T &obj, std::integral_constant) + { + return { obj }; + } + //! \brief Helper class which parses the stringified macro argument. struct Parser { @@ -162,6 +215,15 @@ namespace BlackMisc static std::tuple<> constToTuple(const T &object); //! @} + /*! + * \name Static Private Member Functions + * \brief Returns a tuple of structs, each of which contains a reference to one of object's data members and its attched metadata. + */ + //! @{ + static std::tuple<> toMetaTuple(const T &object); + static std::tuple<> toMetaTuple(T &object); + //! @} + /*! * \name Static Private Member Functions * \brief Returns an object with information extracted from the stringified macro argument. @@ -181,7 +243,7 @@ namespace BlackMisc using ::qHash; /*! - * \brief Works like std::tie, and allows us to hook in our own customizations. + * \brief Works like std::tie, returning a tuple of references to just the values, with metadata removed. * \ingroup Tuples */ template @@ -190,6 +252,16 @@ namespace BlackMisc return std::make_tuple(Private::tieHelper(args)...); } + /*! + * \brief Works like std::tie, returning a tuple of objects, each of which contains metadata plus a reference to a value. + * \ingroup Tuples + */ + template + auto tieMeta(Ts &&... args) -> decltype(std::make_tuple(Private::tieMetaHelper(args)...)) + { + return std::make_tuple(Private::tieMetaHelper(args)...); + } + /*! * \brief Lexicographical tuple comparison function which is CValueObject-aware. * \details Tuple members which are CValueObjects are compared using the compare() friend function of CValueObject; @@ -199,7 +271,10 @@ namespace BlackMisc template int compare(std::tuple a, std::tuple b) { - return Private::TupleHelper::compare(a, b, Private::make_index_sequence()); + auto valuesA = Private::stripMeta(a, Private::make_index_sequence()); + auto valuesB = Private::stripMeta(b, Private::make_index_sequence()); + auto metaTu = Private::recoverMeta(a, Private::make_index_sequence()); + return Private::TupleHelper::compare(valuesA, valuesB, Private::skipFlaggedIndices(metaTu)); } /*! @@ -209,7 +284,9 @@ namespace BlackMisc template QDBusArgument &operator <<(QDBusArgument &arg, std::tuple tu) { - return Private::TupleHelper::marshall(arg, tu, Private::make_index_sequence()); + auto valueTu = Private::stripMeta(tu, Private::make_index_sequence()); + auto metaTu = Private::recoverMeta(tu, Private::make_index_sequence()); + return Private::TupleHelper::marshall(arg, valueTu, Private::skipFlaggedIndices(metaTu)); } /*! @@ -219,7 +296,9 @@ namespace BlackMisc template const QDBusArgument &operator >>(const QDBusArgument &arg, std::tuple tu) { - return Private::TupleHelper::unmarshall(arg, tu, Private::make_index_sequence()); + auto valueTu = Private::stripMeta(tu, Private::make_index_sequence()); + auto metaTu = Private::recoverMeta(tu, Private::make_index_sequence()); + return Private::TupleHelper::unmarshall(arg, valueTu, Private::skipFlaggedIndices(metaTu)); } /*! @@ -229,7 +308,9 @@ namespace BlackMisc template QDebug operator <<(QDebug debug, std::tuple tu) { - return Private::TupleHelper::debug(debug, tu, Private::make_index_sequence()); + auto valueTu = Private::stripMeta(tu, Private::make_index_sequence()); + auto metaTu = Private::recoverMeta(tu, Private::make_index_sequence()); + return Private::TupleHelper::debug(debug, valueTu, Private::skipFlaggedIndices(metaTu)); } /*! @@ -250,7 +331,9 @@ namespace BlackMisc template uint qHash(std::tuple tu) { - return Private::TupleHelper::hash(tu, Private::make_index_sequence()); + auto valueTu = Private::stripMeta(tu, Private::make_index_sequence()); + auto metaTu = Private::recoverMeta(tu, Private::make_index_sequence()); + return Private::TupleHelper::hash(valueTu, Private::skipFlaggedIndices(metaTu)); } /*! @@ -261,18 +344,22 @@ namespace BlackMisc QJsonObject serializeJson(const QStringList &members, std::tuple tu) { QJsonObject json; - Private::TupleHelper::serializeJson(json, members, tu, Private::make_index_sequence()); + auto valueTu = Private::stripMeta(tu, Private::make_index_sequence()); + auto metaTu = Private::recoverMeta(tu, Private::make_index_sequence()); + Private::TupleHelper::serializeJson(json, members, valueTu, Private::skipFlaggedIndices(metaTu)); return json; } /*! - * Convert from JSON to object + * \brief Convert from JSON to object * \ingroup Tuples */ template void deserializeJson(const QJsonObject &json, const QStringList &members, std::tuple tu) { - Private::TupleHelper::deserializeJson(json, members, tu, Private::make_index_sequence()); + auto valueTu = Private::stripMeta(tu, Private::make_index_sequence()); + auto metaTu = Private::recoverMeta(tu, Private::make_index_sequence()); + Private::TupleHelper::deserializeJson(json, members, valueTu, Private::skipFlaggedIndices(metaTu)); } } // namespace BlackMisc diff --git a/src/blackmisc/tuple_private.h b/src/blackmisc/tuple_private.h index 942fa368f..f0b95b23a 100644 --- a/src/blackmisc/tuple_private.h +++ b/src/blackmisc/tuple_private.h @@ -55,13 +55,6 @@ namespace BlackMisc return 0; } - template - int compareHelper(const Tu &a, const Tu &b) - { - typedef typename std::decay::type>::type Element; - return compareHelper(std::get(a), std::get(b), typename std::is_base_of::type()); - } - // Our own implementation of std::index_sequence (because not implemented by MSVC2013) template struct index_sequence @@ -82,12 +75,118 @@ namespace BlackMisc template using make_index_sequence = typename GenSequence<0, C>::type; - // Helper which will allow us to hook in our own customizations into BlackMisc::tie + // 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 quint64 flags = Flags; + + Attribute(T &obj) : m_obj(obj) {} + T &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; } + 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; + } + + // 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)))...); + } // Applying operations to all elements in a tuple, using index_sequence for clean recursion class TupleHelper