/* Copyright (C) 2016 * 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. */ //! \file #ifndef BLACKMISC_METACLASS_H #define BLACKMISC_METACLASS_H #include "blackmisc/metaclassprivate.h" #include "blackmisc/invoke.h" /*! * \defgroup MetaClass Metaclass system * Compile-time reflection toolkit for iterating over * members of value classes. */ /*! * Macro to define a nested metaclass that describes the attributes of its * enclosing class. Use in the private section of the class. * * \tparam CLASS The name of the class containing the member. * \note A semicolon is needed at the end. * \ingroup MetaClass */ #define BLACK_METACLASS(CLASS, ...) \ friend struct BlackMisc::Private::CMetaClassAccessor; \ struct MetaClass : public BlackMisc::CMetaClass \ { \ using Class = CLASS; \ BLACK_NO_EXPORT_CONSTEXPR static auto getMemberList() \ { \ return makeMetaMemberList(__VA_ARGS__); \ } \ } /*! * Macro to define an element within a metaclass. * * Additional arguments can be supplied in the variadic part, which will be * forwarded to CMetaClass::makeMetaMember. * * \tparam MEMBER The name of the member without m_ part. * \see BLACK_METACLASS * \see BLACK_METAMEMBER_NAMED * \see BlackMisc::CMetaClass::makeMetaMember * \ingroup MetaClass */ #define BLACK_METAMEMBER(MEMBER, ...) \ makeMetaMember( \ &Class::m_##MEMBER, #MEMBER BLACK_TRAILING_VA_ARGS(__VA_ARGS__) \ ) /*! * Same as BLACK_METAMEMBER but the second parameter is a string literal * containing the JSON name of the member. * * \ingroup MetaClass */ #define BLACK_METAMEMBER_NAMED(MEMBER, NAME, ...) \ makeMetaMember( \ &Class::m_##MEMBER, NAME BLACK_TRAILING_VA_ARGS(__VA_ARGS__) \ ) namespace BlackMisc { class CVariant; /*! * Type wrapper for passing MetaFlag to CMetaClassIntrospector::with and CMetaClassIntrospector::without. * \ingroup MetaClass */ template struct MetaFlags : public std::integral_constant { //! Implicit conversion to std::false_type (if F is zero) or std::true_type (if F is non-zero). constexpr operator std::integral_constant(F)>() const { return {}; } }; /*! * Compile-time union of MetaFlags. * \ingroup MetaClass */ template constexpr MetaFlags operator |(MetaFlags, MetaFlags) { return {}; } /*! * Compile-time intersection of MetaFlags. * \ingroup MetaClass */ template constexpr MetaFlags operator &(MetaFlags, MetaFlags) { return {}; } /*! * Literal aggregate type representing attributes of one member of a value class. * \ingroup MetaClass */ template struct CMetaMember { //! Pointer to the member. const M m_ptr; //! Member name. const char *const m_name; //! Property index of the member. //! \deprecated Reserved for future use. const int m_index; //! Any flags applying to the member. const MetaFlags m_flags; //! True if m_flags contains all flags. template constexpr bool has(Flags2 flags) const { return (m_flags & flags) == flags; } //! Invoke the member on an instance of the value class. template decltype(auto) in(T &&object, Ts &&... args) const { return Private::invoke(m_ptr, std::forward(object), std::forward(args)...); } //! Return name as QLatin1String. Q_DECL_CONSTEXPR auto latin1Name() const { return QLatin1String(m_name); } }; /*! * Literal aggregate type representing attributes of the members of a value class. * \ingroup MetaClass */ template struct CMetaMemberList { //! Tuple of CMetaMember. const Private::tuple m_members; //! Number of members. static constexpr size_t c_size = sizeof...(Members); //! Convenience method returning the member at index I. template constexpr auto at(std::integral_constant = {}) const BLACK_TRAILING_RETURN(Private::get(m_members)) { return Private::get(m_members); } }; /*! * Metadata flags attached to members of a meta class. * \ingroup MetaClass */ enum MetaFlag { DisabledForComparison = 1 << 0, //!< Element will be ignored by compare() and comparison 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 CaseInsensitiveComparison = 1 << 5 //!< Element will be compared case insensitively (must be a QString) }; /*! * Base class for meta classes. * Just static protected members to be used by derived meta classes. * \ingroup MetaClass */ class CMetaClass { protected: //! Flags wrapped as compile-time constants. //! @{ constexpr static MetaFlags DisabledForComparison {}; constexpr static MetaFlags DisabledForMarshalling {}; constexpr static MetaFlags DisabledForDebugging {}; constexpr static MetaFlags DisabledForHashing {}; constexpr static MetaFlags DisabledForJson {}; constexpr static MetaFlags CaseInsensitiveComparison {}; //! @} //! Return a CMetaMemberList of type deduced from the types of the meta members. //! Usually not used directly, but via the macros. template constexpr static CMetaMemberList makeMetaMemberList(Members... members) { return { Private::tuple(members...) }; } //! Return a CMetaMethod of type deduced from the type of the member. //! Usually not used directly, but via the macros. template constexpr static CMetaMember makeMetaMember(M ptrToMember, const char *name = nullptr, int index = 0, MetaFlags flags = {}) { static_assert(std::is_member_object_pointer::value, "M must be a pointer to member object"); return { ptrToMember, name, index, flags }; } }; /*! * Implementation of an introspector for the metaclass of T. * Obtain an instance of this class via BlackMisc::introspect. * \ingroup MetaClass */ template class CMetaClassIntrospector { public: //! Return a CMetaClassIntrospector covering only those members which have the given flags. //! \see BlackMisc::MetaFlags template static auto with(Flags) { return filter(MaskSequence<(members().at(index()).has(Flags()))...>()); } //! Return a CMetaClassIntrospector covering only those members which do not have the given flags. //! \see BlackMisc::MetaFlags template static auto without(Flags) { return filter(MaskSequence<(! members().at(index()).has(Flags()))...>()); } //! For each metamember in metaclass, pass metamember as argument to visitor function. template static void forEachMember(F &&visitor) { // parameter pack swallow idiom static_cast(std::initializer_list { (static_cast(std::forward(visitor)(members().at(index()))), 0)... }); } private: template using MaskSequence = Private::MaskSequence, Mask...>; template static auto filter(Private::index_sequence) { return CMetaClassIntrospector(); } template using index = std::integral_constant; constexpr static auto members() BLACK_TRAILING_RETURN(MetaClass::getMemberList()) { return MetaClass::getMemberList(); } }; namespace Private { //! \private Friend class of all value classes, so it can access the private nested class. struct CMetaClassAccessor { template static auto getIntrospector(index_sequence) { return CMetaClassIntrospector(); } template static auto getIntrospector() { return getIntrospector(Private::make_index_sequence()); } template static std::true_type hasMetaClass(int, typename T::MetaClass * = nullptr) { return {}; } template static std::false_type hasMetaClass(...) { return {}; } }; } /*! * Obtain the CMetaClassIntrospector for the metaclass of T. * \return BlackMisc::CMetaClassIntrospector * \ingroup MetaClass */ template auto introspect() { return Private::CMetaClassAccessor::getIntrospector(); } /*! * Trait that is true if T has a metaclass. * \ingroup MetaClass */ template struct THasMetaClass : public decltype(Private::CMetaClassAccessor::hasMetaClass(0)) {}; } // namespace #endif