mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-03-30 11:55:35 +08:00
refs #628 Add the first version of the metaclass system.
This commit is contained in:
319
src/blackmisc/metaclass.h
Normal file
319
src/blackmisc/metaclass.h
Normal file
@@ -0,0 +1,319 @@
|
||||
/* 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"
|
||||
#include "blackmisc/tuple.h" // just for TupleConverterFlags
|
||||
|
||||
/*!
|
||||
* \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() \
|
||||
BLACK_TRAILING_RETURN(CMetaClass::makeMetaMemberList(__VA_ARGS__)) \
|
||||
{ \
|
||||
return CMetaClass::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, ...) \
|
||||
CMetaClass::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, ...) \
|
||||
CMetaClass::makeMetaMember( \
|
||||
&Class::m_##MEMBER, NAME BLACK_TRAILING_VA_ARGS(__VA_ARGS__) \
|
||||
)
|
||||
|
||||
namespace BlackMisc
|
||||
{
|
||||
|
||||
class CVariant;
|
||||
|
||||
/*!
|
||||
* Metadata flags attached to members of a meta class.
|
||||
* \todo Remove TupleConverterFlags and change MetaFlag to a full enum.
|
||||
* \ingroup MetaClass
|
||||
*/
|
||||
using MetaFlag = TupleConverterFlags;
|
||||
//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)
|
||||
//};
|
||||
|
||||
/*!
|
||||
* Type wrapper for passing MetaFlag to CMetaClassIntrospector::with and CMetaClassIntrospector::without.
|
||||
* \ingroup MetaClass
|
||||
*/
|
||||
template <quint64 F>
|
||||
using MetaFlags = std::integral_constant<quint64, F>;
|
||||
|
||||
/*!
|
||||
* Literal aggregate type representing attributes of one member of a value class.
|
||||
* \ingroup MetaClass
|
||||
*/
|
||||
template <typename M>
|
||||
struct CMetaMember
|
||||
{
|
||||
//! Pointer to the member.
|
||||
const M m_ptr;
|
||||
|
||||
//! Member name.
|
||||
const char *const m_name;
|
||||
|
||||
//! Property index of the member.
|
||||
//! \todo Not used yet.
|
||||
const int m_index;
|
||||
|
||||
//! Any flags applying to the member.
|
||||
const quint64 m_flags;
|
||||
|
||||
//! True if m_flags contains Flags.
|
||||
template <typename Flags>
|
||||
constexpr bool has(Flags) const { return (m_flags & Flags::value) == Flags::value; }
|
||||
|
||||
//! Invoke the member on an instance of the value class.
|
||||
template <typename T, typename... Ts>
|
||||
decltype(auto) in(T &&object, Ts &&... args) const
|
||||
{
|
||||
return Private::invoke(m_ptr, std::forward<T>(object), std::forward<Ts>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
* Literal aggregate type representing attributes of the members of a value class.
|
||||
* \ingroup MetaClass
|
||||
*/
|
||||
template <typename... Members>
|
||||
struct CMetaMemberList
|
||||
{
|
||||
//! Tuple of CMetaMember.
|
||||
const Private::tuple<Members...> m_members;
|
||||
|
||||
//! Number of members.
|
||||
static constexpr size_t c_size = sizeof...(Members);
|
||||
|
||||
//! Convenience method returning the member at index I.
|
||||
template <size_t I>
|
||||
constexpr auto at(std::integral_constant<size_t, I> = {}) const BLACK_TRAILING_RETURN(Private::get<I>(m_members))
|
||||
{
|
||||
return Private::get<I>(m_members);
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
* Base class for meta classes.
|
||||
* Just static protected members to be used by derived meta classes.
|
||||
* \ingroup MetaClass
|
||||
*/
|
||||
class CMetaClass
|
||||
{
|
||||
protected:
|
||||
//! Return a CMetaMemberList of type deduced from the types of the meta members.
|
||||
//! Usually not used directly, but via the macros.
|
||||
template <typename... Members>
|
||||
constexpr static CMetaMemberList<Members...> makeMetaMemberList(Members... members)
|
||||
{
|
||||
return { { members... } };
|
||||
}
|
||||
|
||||
//! Return a CMetaMethod of type deduced from the type of the member.
|
||||
//! Usually not used directly, but via the macros.
|
||||
template <typename M>
|
||||
constexpr static CMetaMember<M> makeMetaMember(M ptrToMember, const char *name = nullptr, int index = 0, quint64 flags = 0)
|
||||
{
|
||||
static_assert(std::is_member_object_pointer<M>::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 <typename T, typename MetaClass, size_t... Is>
|
||||
class CMetaClassIntrospector
|
||||
{
|
||||
public:
|
||||
//! Return a CMetaClassIntrospector<T> covering only those members which have the given flags.
|
||||
//! \see BlackMisc::MetaFlags
|
||||
template <typename Flags>
|
||||
static auto with(Flags) { return filter(MaskSequence<(members().at(index<Is>()).has(Flags()))...>()); }
|
||||
|
||||
//! Return a CMetaClassIntrospector<T> covering only those members which do not have the given flags.
|
||||
//! \see BlackMisc::MetaFlags
|
||||
template <typename Flags>
|
||||
static auto without(Flags) { return filter(MaskSequence<(! members().at(index<Is>()).has(Flags()))...>()); }
|
||||
|
||||
//! Return a tuple containing references to all members of object.
|
||||
//! @{
|
||||
static auto toTuple(T &object) { return std::tie((members().at(index<Is>()).in(object))...); }
|
||||
static auto toTuple(const T &object) { return std::tie((members().at(index<Is>()).in(object))...); }
|
||||
//! @}
|
||||
|
||||
//! Like toTuple, but members with the CaseInsensitiveComparison flag will be wrapped so that their comparisons are case insensitive.
|
||||
static auto toCaseAwareTuple(const T &object) { return std::make_tuple(caseAwareWrap<Is>(members().at(index<Is>()).in(object))...); }
|
||||
|
||||
//! For each member in object, pass member as argument to visitor function.
|
||||
//! @{
|
||||
template <typename F>
|
||||
static void forEachMember(T &object, F &&visitor)
|
||||
{
|
||||
forEachImpl([ & ](auto &&member) { std::forward<F>(visitor)(member.in(object)); });
|
||||
}
|
||||
template <typename F>
|
||||
static void forEachMember(const T &object, F &&visitor)
|
||||
{
|
||||
forEachImpl([ & ](auto &&member) { std::forward<F>(visitor)(member.in(object)); });
|
||||
}
|
||||
//! @}
|
||||
|
||||
//! For each member in object pair, pass member pair as arguments to visitor function.
|
||||
//! @{
|
||||
template <typename F>
|
||||
static void forEachMemberPair(T &left, T &right, F &&visitor)
|
||||
{
|
||||
forEachImpl([ & ](auto &&member) { std::forward<F>(visitor)(member.in(left), member.in(right)); });
|
||||
}
|
||||
template <typename F>
|
||||
static void forEachMemberPair(const T &left, T &right, F &&visitor)
|
||||
{
|
||||
forEachImpl([ & ](auto &&member) { std::forward<F>(visitor)(member.in(left), member.in(right)); });
|
||||
}
|
||||
template <typename F>
|
||||
static void forEachMemberPair(T &left, const T &right, F &&visitor)
|
||||
{
|
||||
forEachImpl([ & ](auto &&member) { std::forward<F>(visitor)(member.in(left), member.in(right)); });
|
||||
}
|
||||
template <typename F>
|
||||
static void forEachMemberPair(const T &left, const T &right, F &&visitor)
|
||||
{
|
||||
forEachImpl([ & ](auto &&member) { std::forward<F>(visitor)(member.in(left), member.in(right)); });
|
||||
}
|
||||
//! @}
|
||||
|
||||
//! For each member in object, pass member and its name as arguments to visitor function.
|
||||
//! @{
|
||||
template <typename F>
|
||||
static void forEachMemberName(T &object, F &&visitor)
|
||||
{
|
||||
forEachImpl([ & ](auto &&member) { std::forward<F>(visitor)(member.in(object), QString(member.m_name)); });
|
||||
}
|
||||
template <typename F>
|
||||
static void forEachMemberName(const T &object, F &&visitor)
|
||||
{
|
||||
forEachImpl([ & ](auto &&member) { std::forward<F>(visitor)(member.in(object), QString(member.m_name)); });
|
||||
}
|
||||
//! @}
|
||||
|
||||
private:
|
||||
template <bool... Mask>
|
||||
using MaskSequence = Private::MaskSequence<Private::index_sequence<Is...>, Mask...>;
|
||||
|
||||
template <size_t... Js>
|
||||
static auto filter(Private::index_sequence<Js...>) { return CMetaClassIntrospector<T, MetaClass, Js...>(); }
|
||||
|
||||
template <size_t I>
|
||||
using index = std::integral_constant<size_t, I>;
|
||||
|
||||
constexpr static auto members() BLACK_TRAILING_RETURN(MetaClass::getMemberList()) { return MetaClass::getMemberList(); }
|
||||
|
||||
template <typename F>
|
||||
static void forEachImpl(F &&visitor)
|
||||
{
|
||||
// parameter pack swallow idiom
|
||||
static_cast<void>(std::initializer_list<int> { (static_cast<void>(std::forward<F>(visitor)(members().at(index<Is>()))), 0)... });
|
||||
}
|
||||
|
||||
template <size_t I, typename U>
|
||||
static auto caseAwareWrap(const U &value)
|
||||
{
|
||||
using IsCaseInsensitive = std::integral_constant<bool, members().at(index<I>()).has(MetaFlags<CaseInsensitiveComparison>())>;
|
||||
return Private::caseAwareWrap(IsCaseInsensitive(), value);
|
||||
}
|
||||
};
|
||||
|
||||
namespace Private
|
||||
{
|
||||
//! \private Friend class of all value classes, so it can access the private nested class.
|
||||
struct CMetaClassAccessor
|
||||
{
|
||||
template <typename T, size_t... Is>
|
||||
static auto getIntrospector(index_sequence<Is...>)
|
||||
{
|
||||
return CMetaClassIntrospector<T, typename T::MetaClass, Is...>();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static auto getIntrospector()
|
||||
{
|
||||
return getIntrospector<T>(Private::make_index_sequence<T::MetaClass::getMemberList().c_size>());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/*!
|
||||
* Obtain the CMetaClassIntrospector for the metaclass of T.
|
||||
* \return BlackMisc::CMetaClassIntrospector
|
||||
* \ingroup MetaClass
|
||||
*/
|
||||
template <typename T>
|
||||
auto introspect()
|
||||
{
|
||||
return Private::CMetaClassAccessor::getIntrospector<T>();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
139
src/blackmisc/metaclassprivate.h
Normal file
139
src/blackmisc/metaclassprivate.h
Normal file
@@ -0,0 +1,139 @@
|
||||
/* 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_METACLASSPRIVATE_H
|
||||
#define BLACKMISC_METACLASSPRIVATE_H
|
||||
|
||||
#include "integersequence.h"
|
||||
#include <type_traits>
|
||||
#include <functional>
|
||||
#include <QString>
|
||||
|
||||
//! \cond PRIVATE
|
||||
|
||||
// GCC 4.9 doesn't utilize constexpr in the standard library
|
||||
#if ! (defined(Q_CC_GNU) && __GNUC__ <= 4)
|
||||
#define BLACK_HAS_CONSTEXPR_STDLIB
|
||||
#endif
|
||||
|
||||
// Work around MinGW problem with combination of constexpr and extern template
|
||||
#if defined(Q_OS_WIN) && defined(Q_CC_GNU)
|
||||
#define BLACK_NO_EXPORT_CONSTEXPR constexpr inline __attribute__((gnu_inline))
|
||||
#else
|
||||
#define BLACK_NO_EXPORT_CONSTEXPR constexpr
|
||||
#endif
|
||||
|
||||
// MSVC, GCC, Clang all have non-standard extensions for skipping trailing
|
||||
// commas in variadic macros, but the MSVC extension differs from the others.
|
||||
#ifdef Q_CC_MSVC
|
||||
#define BLACK_TRAILING_VA_ARGS(...) ,__VA_ARGS__
|
||||
#else
|
||||
#define BLACK_TRAILING_VA_ARGS(...) ,##__VA_ARGS__
|
||||
#endif
|
||||
|
||||
// Work around MSVC constexpr bug
|
||||
// https://connect.microsoft.com/VisualStudio/feedback/details/2028721
|
||||
#ifdef Q_CC_MSVC
|
||||
#define BLACK_DECLTYPE_AUTO auto
|
||||
#define BLACK_TRAILING_RETURN(EXPR) -> decltype(EXPR)
|
||||
#else
|
||||
#define BLACK_DECLTYPE_AUTO decltype(auto)
|
||||
#define BLACK_TRAILING_RETURN(EXPR)
|
||||
#endif
|
||||
|
||||
namespace BlackMisc
|
||||
{
|
||||
namespace Private
|
||||
{
|
||||
#ifdef BLACK_HAS_CONSTEXPR_STDLIB
|
||||
using std::tuple;
|
||||
using std::tuple_size;
|
||||
using std::get;
|
||||
using std::make_tuple;
|
||||
#else // Own implementation of tuple, because the one in GCC 4.9 is not constexpr.
|
||||
template <size_t I, typename Head, typename... Tail>
|
||||
struct conslist;
|
||||
|
||||
template <size_t I, typename Head>
|
||||
struct conslist<I, Head>
|
||||
{
|
||||
constexpr conslist() {}
|
||||
constexpr conslist(const Head &v) : m_obj(v) {}
|
||||
constexpr const Head &get(std::integral_constant<size_t, I>) const { return m_obj; }
|
||||
Head m_obj;
|
||||
};
|
||||
|
||||
template <size_t I, typename Head, typename... Tail>
|
||||
struct conslist : public conslist<I + 1, Tail...>
|
||||
{
|
||||
constexpr conslist() {}
|
||||
constexpr conslist(const Head &v, const Tail &... vs) : conslist<I + 1, Tail...>(vs...), m_obj(v) {}
|
||||
constexpr const Head &get(std::integral_constant<size_t, I>) const { return m_obj; }
|
||||
using conslist<I + 1, Tail...>::get;
|
||||
Head m_obj;
|
||||
};
|
||||
|
||||
template <typename... Ts>
|
||||
struct tuple
|
||||
{
|
||||
constexpr tuple() {}
|
||||
constexpr tuple(const Ts &... vs) : m_conslist(vs...) {}
|
||||
conslist<0, Ts...> m_conslist;
|
||||
constexpr static size_t c_size = sizeof...(Ts);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct tuple_size : public std::integral_constant<size_t, T::c_size> {};
|
||||
|
||||
template <size_t I, typename T>
|
||||
constexpr decltype(auto) get(T &&tuple) { return std::forward<T>(tuple).m_conslist.get(std::integral_constant<size_t, I>()); }
|
||||
|
||||
template <typename... Ts>
|
||||
constexpr auto make_tuple(Ts &&... vs) { return tuple<std::decay_t<Ts>...>(std::forward<Ts>(vs)...); }
|
||||
#endif // ! BLACK_HAS_CONSTEXPR_STDLIB
|
||||
|
||||
// Helper for case insensitive comparisons.
|
||||
template <typename T>
|
||||
struct CaseInsensitiveWrapper
|
||||
{
|
||||
const T &m_ref;
|
||||
|
||||
explicit CaseInsensitiveWrapper(const T &ref) : m_ref(ref) {}
|
||||
|
||||
friend int compare(CaseInsensitiveWrapper a, CaseInsensitiveWrapper b)
|
||||
{
|
||||
return a.m_ref.compare(b.m_ref, Qt::CaseInsensitive);
|
||||
}
|
||||
friend bool operator ==(CaseInsensitiveWrapper a, CaseInsensitiveWrapper b)
|
||||
{
|
||||
return compare(a, b) == 0;
|
||||
}
|
||||
friend bool operator <(CaseInsensitiveWrapper a, CaseInsensitiveWrapper b)
|
||||
{
|
||||
return compare(a, b) < 0;
|
||||
}
|
||||
};
|
||||
template <typename T>
|
||||
auto caseAwareWrap(std::false_type, const T &value)
|
||||
{
|
||||
return std::cref(value);
|
||||
}
|
||||
template <typename T>
|
||||
auto caseAwareWrap(std::true_type, const T &value)
|
||||
{
|
||||
return CaseInsensitiveWrapper<T>(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//! \endcond
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user