Updates from BitShares FC #22
8 changed files with 669 additions and 294 deletions
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
/**
|
||||
* @file fc/reflect.hpp
|
||||
* @file fc/reflect/reflect.hpp
|
||||
*
|
||||
* @brief Defines types and macros used to provide reflection.
|
||||
*
|
||||
|
|
@ -18,9 +18,114 @@
|
|||
#include <type_traits>
|
||||
|
||||
#include <fc/reflect/typename.hpp>
|
||||
#include <fc/reflect/typelist.hpp>
|
||||
|
||||
namespace fc {
|
||||
|
||||
template<typename> struct reflector;
|
||||
namespace member_names {
|
||||
/// A template which stores the name of the native member at a given index in a given class
|
||||
template<typename Class, std::size_t index> struct member_name {
|
||||
constexpr static const char* value = "Unknown member";
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A template to store compile-time information about a field in a reflected struct
|
||||
*
|
||||
* @tparam Container The type of the struct or class containing the field
|
||||
* @tparam Member The type of the field
|
||||
* @tparam field A pointer-to-member for the reflected field
|
||||
*/
|
||||
template<std::size_t Index, typename Container, typename Member, Member Container::*field>
|
||||
struct field_reflection {
|
||||
using container = Container;
|
||||
using type = Member;
|
||||
using reflector = fc::reflector<type>;
|
||||
constexpr static std::size_t index = Index;
|
||||
constexpr static bool is_derived = false;
|
||||
constexpr static type container::*pointer = field;
|
||||
|
||||
/// @brief Given a reference to the container type, get a reference to the field
|
||||
static type& get(container& c) { return c.*field; }
|
||||
static const type& get(const container& c) { return c.*field; }
|
||||
/// @brief Get the name of the field
|
||||
static const char* get_name() { return fc::member_names::member_name<container, index>::value; }
|
||||
};
|
||||
/// Basically the same as @ref field_reflection, but for inherited fields
|
||||
/// Note that inherited field reflections do not have an index field; indexes are for native fields only
|
||||
template<std::size_t IndexInBase, typename Base, typename Derived, typename Member, Member Base::*field>
|
||||
struct inherited_field_reflection {
|
||||
using container = Derived;
|
||||
using field_container = Base;
|
||||
using type = Member;
|
||||
using reflector = fc::reflector<type>;
|
||||
constexpr static std::size_t index_in_base = IndexInBase;
|
||||
constexpr static bool is_derived = true;
|
||||
constexpr static type field_container::*pointer = field;
|
||||
|
||||
static type& get(container& c) {
|
||||
// And we need a distinct inherited_field_reflection type because this conversion can't be done statically
|
||||
type container::* derived_field = field;
|
||||
return c.*derived_field;
|
||||
}
|
||||
static const type& get(const container& c) {
|
||||
type container::* derived_field = field;
|
||||
return c.*derived_field;
|
||||
}
|
||||
static const char* get_name() {
|
||||
using Reflector = typename fc::reflector<Base>::native_members::template at<IndexInBase>;
|
||||
return Reflector::get_name();
|
||||
}
|
||||
};
|
||||
|
||||
namespace impl {
|
||||
/// Helper template to create a @ref field_reflection without any commas (makes it macro-friendly)
|
||||
template<typename Container>
|
||||
struct Reflect_type {
|
||||
template<typename Member>
|
||||
struct with_field_type {
|
||||
template<std::size_t Index>
|
||||
struct at_index {
|
||||
template<Member Container::*field>
|
||||
struct with_field_pointer {
|
||||
using type = field_reflection<Index, Container, Member, field>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
/// Template to make a transformer of a @ref field_reflection from a base class to a derived class
|
||||
template<typename Derived>
|
||||
struct Derivation_reflection_transformer {
|
||||
template<typename> struct transform;
|
||||
template<std::size_t IndexInBase, typename BaseContainer, typename Member, Member BaseContainer::*field>
|
||||
struct transform<field_reflection<IndexInBase, BaseContainer, Member, field>> {
|
||||
using type = inherited_field_reflection<IndexInBase, BaseContainer, Derived, Member, field>;
|
||||
};
|
||||
template<std::size_t IndexInBase, typename BaseContainer, typename IntermediateContainer,
|
||||
typename Member, Member BaseContainer::*field>
|
||||
struct transform<inherited_field_reflection<IndexInBase, BaseContainer, IntermediateContainer, Member, field>> {
|
||||
using type = inherited_field_reflection<IndexInBase, BaseContainer, Derived, Member, field>;
|
||||
};
|
||||
};
|
||||
} // namespace impl
|
||||
|
||||
/// Macro to transform reflected fields of a base class to a derived class and concatenate them to a type list
|
||||
#define FC_CONCAT_BASE_MEMBER_REFLECTIONS(r, derived, base) \
|
||||
::add_list<typelist::transform<reflector<base>::members, impl::Derivation_reflection_transformer<derived>>>
|
||||
/// Macro to concatenate a new @ref field_reflection to a typelist
|
||||
#define FC_CONCAT_MEMBER_REFLECTION(r, container, idx, member) \
|
||||
::add<typename impl::Reflect_type<container>::template with_field_type<decltype(container::member)> \
|
||||
::template at_index<idx> \
|
||||
::template with_field_pointer<&container::member>::type>
|
||||
#define FC_REFLECT_MEMBER_NAME(r, container, idx, member) \
|
||||
template<> struct member_name<container, idx> { constexpr static const char* value = BOOST_PP_STRINGIZE(member); };
|
||||
#define FC_REFLECT_TEMPLATE_MEMBER_NAME(r, data, idx, member) \
|
||||
template<BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_ELEM(0, data))> struct member_name<BOOST_PP_SEQ_ELEM(1, data), idx> { \
|
||||
constexpr static const char* value = BOOST_PP_STRINGIZE(member); };
|
||||
/// Macro to concatenate a new type to a typelist
|
||||
#define FC_CONCAT_TYPE(r, x, TYPE) ::add<TYPE>
|
||||
|
||||
/**
|
||||
* @brief defines visit functions for T
|
||||
* Unless this is specialized, visit() will not be defined for T.
|
||||
|
|
@ -34,6 +139,14 @@ template<typename T>
|
|||
struct reflector{
|
||||
typedef T type;
|
||||
typedef std::false_type is_defined;
|
||||
/// A typelist with a @ref field_reflection for each native member (non-inherited) of the struct
|
||||
using native_members = typelist::list<>;
|
||||
/// A typelist with a @ref field_reflection for each inherited member of the struct
|
||||
using inherited_members = typelist::list<>;
|
||||
/// A typelist with a @ref field_reflection for each member of the struct, starting with inherited members
|
||||
using members = typelist::list<>;
|
||||
/// A typelist of base classes for this type
|
||||
using base_classes = typelist::list<>;
|
||||
|
||||
/**
|
||||
* @tparam Visitor a function object of the form:
|
||||
|
|
@ -91,31 +204,11 @@ void throw_bad_enum_cast( const char* k, const char* e );
|
|||
case I: FC_REFLECT_VISIT_MEMBER( r, visitor, elem ) break;
|
||||
|
||||
|
||||
#define FC_REFLECT_BASE_MEMBER_COUNT( r, OP, elem ) \
|
||||
OP fc::reflector<elem>::total_member_count
|
||||
|
||||
#define FC_REFLECT_MEMBER_COUNT( r, OP, elem ) \
|
||||
OP 1
|
||||
|
||||
#define FC_REFLECT_DERIVED_IMPL_INLINE( TYPE, INHERITS, MEMBERS ) \
|
||||
template<typename Visitor>\
|
||||
static inline void visit( const Visitor& v ) { \
|
||||
BOOST_PP_SEQ_FOR_EACH( FC_REFLECT_VISIT_BASE, v, INHERITS ) \
|
||||
BOOST_PP_SEQ_FOR_EACH( FC_REFLECT_VISIT_MEMBER, v, MEMBERS ) \
|
||||
}\
|
||||
template<typename Visitor, typename IndexType>\
|
||||
static inline void visit_local_member( const Visitor& v, IndexType index ) { \
|
||||
switch( index ) {\
|
||||
BOOST_PP_SEQ_FOR_EACH_I( FC_REFLECT_VISIT_MEMBER_I, v, MEMBERS ) \
|
||||
default: break;\
|
||||
}\
|
||||
}
|
||||
|
||||
#define FC_REFLECT_DERIVED_IMPL_EXT( TYPE, INHERITS, MEMBERS ) \
|
||||
template<typename Visitor>\
|
||||
void fc::reflector<TYPE>::visit( const Visitor& v ) { \
|
||||
BOOST_PP_SEQ_FOR_EACH( FC_REFLECT_VISIT_BASE, v, INHERITS ) \
|
||||
BOOST_PP_SEQ_FOR_EACH( FC_REFLECT_VISIT_MEMBER, v, MEMBERS ) \
|
||||
}
|
||||
|
||||
#endif // DOXYGEN
|
||||
|
|
@ -214,26 +307,50 @@ namespace fc { \
|
|||
template<> struct reflector<TYPE> {\
|
||||
typedef TYPE type; \
|
||||
typedef std::true_type is_defined; \
|
||||
using native_members = \
|
||||
typename typelist::builder<>::type \
|
||||
BOOST_PP_SEQ_FOR_EACH_I( FC_CONCAT_MEMBER_REFLECTION, TYPE, MEMBERS ) ::finalize; \
|
||||
using inherited_members = \
|
||||
typename typelist::builder<>::type \
|
||||
BOOST_PP_SEQ_FOR_EACH( FC_CONCAT_BASE_MEMBER_REFLECTIONS, TYPE, INHERITS ) ::finalize; \
|
||||
using members = typename typelist::concat<inherited_members, native_members>::type; \
|
||||
using base_classes = typename typelist::builder<>::type \
|
||||
BOOST_PP_SEQ_FOR_EACH( FC_CONCAT_TYPE, x, INHERITS ) ::finalize; \
|
||||
enum member_count_enum { \
|
||||
local_member_count = 0 BOOST_PP_SEQ_FOR_EACH( FC_REFLECT_MEMBER_COUNT, +, MEMBERS ),\
|
||||
total_member_count = local_member_count BOOST_PP_SEQ_FOR_EACH( FC_REFLECT_BASE_MEMBER_COUNT, +, INHERITS )\
|
||||
local_member_count = typelist::length<native_members>(), \
|
||||
total_member_count = typelist::length<members>() \
|
||||
}; \
|
||||
FC_REFLECT_DERIVED_IMPL_INLINE( TYPE, INHERITS, MEMBERS ) \
|
||||
}; }
|
||||
}; \
|
||||
namespace member_names { \
|
||||
BOOST_PP_SEQ_FOR_EACH_I( FC_REFLECT_MEMBER_NAME, TYPE, MEMBERS ) \
|
||||
} }
|
||||
#define FC_REFLECT_DERIVED_TEMPLATE( TEMPLATE_ARGS, TYPE, INHERITS, MEMBERS ) \
|
||||
namespace fc { \
|
||||
template<BOOST_PP_SEQ_ENUM(TEMPLATE_ARGS)> struct get_typename<TYPE> { static const char* name() { return BOOST_PP_STRINGIZE(TYPE); } }; \
|
||||
template<BOOST_PP_SEQ_ENUM(TEMPLATE_ARGS)> struct get_typename<TYPE> { \
|
||||
static const char* name() { return BOOST_PP_STRINGIZE(TYPE); } \
|
||||
}; \
|
||||
template<BOOST_PP_SEQ_ENUM(TEMPLATE_ARGS)> struct reflector<TYPE> {\
|
||||
typedef TYPE type; \
|
||||
typedef std::true_type is_defined; \
|
||||
using native_members = \
|
||||
typename typelist::builder<>::type \
|
||||
BOOST_PP_SEQ_FOR_EACH_I( FC_CONCAT_MEMBER_REFLECTION, TYPE, MEMBERS ) ::finalize; \
|
||||
using inherited_members = \
|
||||
typename typelist::builder<>::type \
|
||||
BOOST_PP_SEQ_FOR_EACH( FC_CONCAT_BASE_MEMBER_REFLECTIONS, TYPE, INHERITS ) ::finalize; \
|
||||
using members = typename typelist::concat<inherited_members, native_members>::type; \
|
||||
using base_classes = typename typelist::builder<>::type \
|
||||
BOOST_PP_SEQ_FOR_EACH( FC_CONCAT_TYPE, x, INHERITS ) ::finalize; \
|
||||
enum member_count_enum { \
|
||||
local_member_count = 0 BOOST_PP_SEQ_FOR_EACH( FC_REFLECT_MEMBER_COUNT, +, MEMBERS ),\
|
||||
total_member_count = local_member_count BOOST_PP_SEQ_FOR_EACH( FC_REFLECT_BASE_MEMBER_COUNT, +, INHERITS )\
|
||||
local_member_count = typelist::length<native_members>(), \
|
||||
total_member_count = typelist::length<members>() \
|
||||
}; \
|
||||
FC_REFLECT_DERIVED_IMPL_INLINE( TYPE, INHERITS, MEMBERS ) \
|
||||
}; }
|
||||
|
||||
//BOOST_PP_SEQ_SIZE(MEMBERS),
|
||||
}; \
|
||||
namespace member_names { \
|
||||
BOOST_PP_SEQ_FOR_EACH_I( FC_REFLECT_TEMPLATE_MEMBER_NAME, (TEMPLATE_ARGS)(TYPE), MEMBERS ) \
|
||||
} }
|
||||
|
||||
/**
|
||||
* @def FC_REFLECT(TYPE,MEMBERS)
|
||||
|
|
@ -244,7 +361,8 @@ template<BOOST_PP_SEQ_ENUM(TEMPLATE_ARGS)> struct reflector<TYPE> {\
|
|||
* @see FC_REFLECT_DERIVED
|
||||
*/
|
||||
#define FC_REFLECT( TYPE, MEMBERS ) \
|
||||
FC_REFLECT_DERIVED( TYPE, BOOST_PP_SEQ_NIL, MEMBERS )
|
||||
FC_REFLECT_DERIVED( TYPE, BOOST_PP_SEQ_NIL, MEMBERS )
|
||||
|
||||
|
||||
#define FC_REFLECT_TEMPLATE( TEMPLATE_ARGS, TYPE, MEMBERS ) \
|
||||
FC_REFLECT_DERIVED_TEMPLATE( TEMPLATE_ARGS, TYPE, BOOST_PP_SEQ_NIL, MEMBERS )
|
||||
|
|
@ -257,25 +375,3 @@ namespace fc { \
|
|||
template<> struct get_typename<TYPE> { static const char* name() { return BOOST_PP_STRINGIZE(TYPE); } }; \
|
||||
}
|
||||
|
||||
#define FC_REFLECT_FWD( TYPE ) \
|
||||
namespace fc { \
|
||||
template<> struct get_typename<TYPE> { static const char* name() { return BOOST_PP_STRINGIZE(TYPE); } }; \
|
||||
template<> struct reflector<TYPE> {\
|
||||
typedef TYPE type; \
|
||||
typedef std::true_type is_defined; \
|
||||
enum member_count_enum { \
|
||||
local_member_count = BOOST_PP_SEQ_SIZE(MEMBERS), \
|
||||
total_member_count = local_member_count BOOST_PP_SEQ_FOR_EACH( FC_REFLECT_BASE_MEMBER_COUNT, +, INHERITS )\
|
||||
}; \
|
||||
template<typename Visitor> static void visit( const Visitor& v ); \
|
||||
}; }
|
||||
|
||||
|
||||
#define FC_REFLECT_DERIVED_IMPL( TYPE, MEMBERS ) \
|
||||
FC_REFLECT_IMPL_DERIVED_EXT( TYPE, BOOST_PP_SEQ_NIL, MEMBERS )
|
||||
|
||||
#define FC_REFLECT_IMPL( TYPE, MEMBERS ) \
|
||||
FC_REFLECT_DERIVED_IMPL_EXT( TYPE, BOOST_PP_SEQ_NIL, MEMBERS )
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
262
include/fc/reflect/typelist.hpp
Normal file
262
include/fc/reflect/typelist.hpp
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
#pragma once
|
||||
/**
|
||||
* @file fc/reflect/typelist.hpp
|
||||
*
|
||||
* @brief Defines a template for manipulating and storing compile-time lists of types
|
||||
*/
|
||||
|
||||
#include <type_traits>
|
||||
#include <functional>
|
||||
|
||||
namespace fc {
|
||||
|
||||
/// This namespace contains the list type, and all of the operations and queries which can be performed upon it
|
||||
namespace typelist {
|
||||
|
||||
// Forward declare the list so impl can see it
|
||||
template<typename...> struct list;
|
||||
|
||||
namespace impl {
|
||||
using typelist::list;
|
||||
|
||||
template<typename, template<typename...> class> struct apply;
|
||||
template<typename... Ts, template<typename...> class Delegate>
|
||||
struct apply<list<Ts...>, Delegate> { using type = Delegate<Ts...>; };
|
||||
|
||||
template<typename... Ts>
|
||||
struct length;
|
||||
template<> struct length<> { constexpr static std::size_t value = 0; };
|
||||
template<typename T, typename... Ts>
|
||||
struct length<T, Ts...> { constexpr static std::size_t value = length<Ts...>::value+1; };
|
||||
|
||||
template<typename...> struct concat;
|
||||
template<typename... OldTypes, typename... NewTypes>
|
||||
struct concat<list<OldTypes...>, list<NewTypes...>> {
|
||||
using type = list<OldTypes..., NewTypes...>;
|
||||
};
|
||||
template<typename... OldTypes, typename... NewTypes, typename NextList, typename... Lists>
|
||||
struct concat<list<OldTypes...>, list<NewTypes...>, NextList, Lists...> {
|
||||
using type = typename concat<list<OldTypes..., NewTypes...>, NextList, Lists...>::type;
|
||||
};
|
||||
|
||||
template<std::size_t count> struct make_sequence;
|
||||
template<> struct make_sequence<0> { using type = list<>; };
|
||||
template<> struct make_sequence<1> { using type = list<std::integral_constant<std::size_t, 0>>; };
|
||||
template<std::size_t count>
|
||||
struct make_sequence {
|
||||
using type = typename concat<typename make_sequence<count-1>::type,
|
||||
list<std::integral_constant<std::size_t, count-1>>>::type;
|
||||
};
|
||||
|
||||
template<typename, typename> struct transform;
|
||||
template<typename... List, typename Transformer>
|
||||
struct transform<list<List...>, Transformer> {
|
||||
using type = list<typename Transformer::template transform<List>::type...>;
|
||||
};
|
||||
|
||||
template<typename Search, typename List> struct index_of;
|
||||
template<typename Search> struct index_of<Search, list<>> { constexpr static int value = -1; };
|
||||
template<typename Search, typename T, typename... Ts>
|
||||
struct index_of<Search, list<T, Ts...>> {
|
||||
constexpr static int deeper = index_of<Search, list<Ts...>>::value;
|
||||
constexpr static int value = std::is_same<Search, T>::value? 0 : (deeper == -1? -1 : deeper + 1);
|
||||
};
|
||||
|
||||
template<typename...> struct concat_unique;
|
||||
template<typename... Uniques>
|
||||
struct concat_unique<list<Uniques...>, list<>> {
|
||||
using type = list<Uniques...>;
|
||||
};
|
||||
template<typename... Uniques, typename T>
|
||||
struct concat_unique<list<Uniques...>, list<T>> {
|
||||
using type = std::conditional_t<index_of<T, list<Uniques...>>::value >= 0,
|
||||
list<Uniques...>, list<Uniques..., T>>;
|
||||
};
|
||||
template<typename... Uniques, typename T1, typename T2, typename... Types>
|
||||
struct concat_unique<list<Uniques...>, list<T1, T2, Types...>> {
|
||||
using type = typename concat_unique<
|
||||
typename concat_unique<list<Uniques...>, list<T1>>::type, list<T2, Types...>>::type;
|
||||
};
|
||||
template<typename... Uniques, typename... Lists>
|
||||
struct concat_unique<list<Uniques...>, list<>, Lists...> {
|
||||
using type = typename concat_unique<list<Uniques...>, Lists...>::type;
|
||||
};
|
||||
template<typename Uniques, typename L1a, typename... L1s, typename L2, typename... Lists>
|
||||
struct concat_unique<Uniques, list<L1a, L1s...>, L2, Lists...> {
|
||||
using type = typename concat_unique<typename concat_unique<Uniques, list<L1a, L1s...>>::type, L2, Lists...>::type;
|
||||
};
|
||||
|
||||
template<typename, std::size_t> struct at;
|
||||
template<typename T, typename... Types>
|
||||
struct at<list<T, Types...>, 0> { using type = T; };
|
||||
template<typename T, typename... Types, std::size_t index>
|
||||
struct at<list<T, Types...>, index> : at<list<Types...>, index-1> {};
|
||||
|
||||
template<typename, typename, std::size_t> struct remove_at;
|
||||
template<typename... Left, typename T, typename... Right>
|
||||
struct remove_at<list<Left...>, list<T, Right...>, 0> { using type = list<Left..., Right...>; };
|
||||
template<typename... Left, typename T, typename... Right, std::size_t index>
|
||||
struct remove_at<list<Left...>, list<T, Right...>, index> {
|
||||
using type = typename remove_at<list<Left..., T>, list<Right...>, index-1>::type;
|
||||
};
|
||||
|
||||
template<template<typename> class Filter, typename Filtered, typename List> struct filter;
|
||||
template<template<typename> class Filter, typename... Filtered>
|
||||
struct filter<Filter, list<Filtered...>, list<>> { using type = list<Filtered...>; };
|
||||
template<template<typename> class Filter, typename... Filtered, typename T1, typename... Types>
|
||||
struct filter<Filter, list<Filtered...>, list<T1, Types...>> {
|
||||
using type = typename std::conditional_t<Filter<T1>::value,
|
||||
filter<Filter, list<Filtered..., T1>, list<Types...>>,
|
||||
filter<Filter, list<Filtered...>, list<Types...>>>::type;
|
||||
};
|
||||
|
||||
template<typename, typename, std::size_t, std::size_t, typename = void> struct slice;
|
||||
template<typename... Results, typename... Types, std::size_t index>
|
||||
struct slice<list<Results...>, list<Types...>, index, index, void> { using type = list<Results...>; };
|
||||
template<typename... Results, typename T, typename... Types, std::size_t end>
|
||||
struct slice<list<Results...>, list<T, Types...>, 0, end, std::enable_if_t<end != 0>>
|
||||
: slice<list<Results..., T>, list<Types...>, 0, end-1> {};
|
||||
template<typename T, typename... Types, std::size_t start, std::size_t end>
|
||||
struct slice<list<>, list<T, Types...>, start, end, std::enable_if_t<start != 0>>
|
||||
: slice<list<>, list<Types...>, start-1, end-1> {};
|
||||
|
||||
template<typename, typename> struct zip;
|
||||
template<>
|
||||
struct zip<list<>, list<>> { using type = list<>; };
|
||||
template<typename A, typename... As, typename B, typename... Bs>
|
||||
struct zip<list<A, As...>, list<B, Bs...>> {
|
||||
using type = typename concat<list<list<A, B>>, typename zip<list<As...>, list<Bs...>>::type>::type;
|
||||
};
|
||||
|
||||
template<typename Callable, typename Ret, typename T>
|
||||
Ret dispatch_helper(Callable& c) { return c(T()); }
|
||||
|
||||
} // namespace impl
|
||||
|
||||
/// The actual list type
|
||||
template<typename... Types>
|
||||
struct list { using type = list; };
|
||||
|
||||
/// Apply a list of types as arguments to another template
|
||||
template<typename List, template<typename...> class Delegate>
|
||||
using apply = typename impl::apply<List, Delegate>::type;
|
||||
|
||||
/// Get the number of types in a list
|
||||
template<typename List>
|
||||
constexpr static std::size_t length() { return apply<List, impl::length>::value; }
|
||||
|
||||
/// Concatenate two or more typelists together
|
||||
template<typename... Lists>
|
||||
using concat = typename impl::concat<Lists...>::type;
|
||||
|
||||
/// Create a list of sequential integers ranging from [0, count)
|
||||
template<std::size_t count>
|
||||
using make_sequence = typename impl::make_sequence<count>::type;
|
||||
|
||||
/// Template to build typelists using the following syntax:
|
||||
/// builder<>::type::add<T1>::add<T2>::add<T3>[...]::finalize
|
||||
/// Or:
|
||||
/// builder<>::type::add_list<list<T1, T2>>::add_list<T3, T4>>[...]::finalize
|
||||
template<typename List = list<>>
|
||||
struct builder {
|
||||
template<typename NewType> using add = typename builder<typename impl::concat<List, list<NewType>>::type>::type;
|
||||
template<typename NewList> using add_list = typename builder<typename impl::concat<List, NewList>::type>::type;
|
||||
using type = builder;
|
||||
using finalize = List;
|
||||
};
|
||||
|
||||
/// Transform elements of a typelist
|
||||
template<typename List, typename Transformer>
|
||||
using transform = typename impl::transform<List, Transformer>::type;
|
||||
|
||||
/// Get the index of the given type within a list, or -1 if type is not found
|
||||
template<typename List, typename T>
|
||||
constexpr static int index_of() { return impl::index_of<T, List>::value; }
|
||||
|
||||
/// Check if a given type is in a list
|
||||
template<typename List, typename T>
|
||||
constexpr static bool contains() { return impl::index_of<T, List>::value != -1; }
|
||||
|
||||
/// Remove duplicate items from one or more typelists and concatenate them all together
|
||||
template<typename... TypeLists>
|
||||
using concat_unique = typename impl::concat_unique<list<>, TypeLists...>::type;
|
||||
|
||||
/// Get the type at the specified list index
|
||||
template<typename List, std::size_t index>
|
||||
using at = typename impl::at<List, index>::type;
|
||||
|
||||
/// Get the type at the beginning of the list
|
||||
template<typename List>
|
||||
using first = at<List, 0>;
|
||||
/// Get the type at the end of the list
|
||||
template<typename List>
|
||||
using last = at<List, length<List>()-1>;
|
||||
|
||||
/// Get the list with the element at the given index removed
|
||||
template<typename List, std::size_t index>
|
||||
using remove_at = typename impl::remove_at<list<>, List, index>::type;
|
||||
|
||||
/// Get the list with the given type removed
|
||||
template<typename List, typename Remove>
|
||||
using remove_element = remove_at<List, index_of<List, Remove>()>;
|
||||
|
||||
/// Get a list with all elements that do not pass a filter removed
|
||||
template<typename List, template<typename> class Filter>
|
||||
using filter = typename impl::filter<Filter, list<>, List>::type;
|
||||
|
||||
/// Template to invert a filter, i.e. filter<mylist, filter_inverter<myfilter>::type>
|
||||
template<template<typename> class Filter>
|
||||
struct invert_filter {
|
||||
template<typename T>
|
||||
struct type { constexpr static bool value = !Filter<T>::value; };
|
||||
};
|
||||
|
||||
/// Take the sublist at indexes [start, end)
|
||||
template<typename List, std::size_t start, std::size_t end = length<List>()>
|
||||
using slice = typename impl::slice<list<>, List, start, end>::type;
|
||||
|
||||
/// Zip two equal-length typelists together, i.e. zip<list<X, Y>, list<A, B>> == list<list<X, A>, list<Y, B>>
|
||||
template<typename ListA, typename ListB>
|
||||
using zip = typename impl::zip<ListA, ListB>::type;
|
||||
|
||||
/// Add indexes to types in the list, i.e. index<list<A, B, C>> == list<list<0, A>, list<1, B>, list<2, C>> where
|
||||
/// 0, 1, and 2 are std::integral_constants of type std::size_t
|
||||
template<typename List>
|
||||
using index = typename impl::zip<typename impl::make_sequence<length<List>()>::type, List>::type;
|
||||
|
||||
/// This namespace contains some utilities that provide runtime operations on typelists
|
||||
namespace runtime {
|
||||
/// Type wrapper object allowing arbitrary types to be passed to functions as information rather than data
|
||||
template<typename T> struct wrapper { using type = T; };
|
||||
|
||||
/**
|
||||
* @brief Index into the typelist for a type T, and invoke the callable with an argument wrapper<T>()
|
||||
* @param index Index of the type in the typelist to invoke the callable with
|
||||
* @param c The callable to invoke
|
||||
* @return The value returned by the callable
|
||||
* @note The callable return type must be the same for all list elements
|
||||
*
|
||||
* If index is out of bounds, throws std::out_of_range exception
|
||||
*/
|
||||
template<typename... Types, typename Callable, typename = std::enable_if_t<impl::length<Types...>::value != 0>,
|
||||
typename Return = decltype(std::declval<Callable>()(wrapper<at<list<Types...>, 0>>()))>
|
||||
Return dispatch(list<Types...>, std::size_t index, Callable c) {
|
||||
static std::function<Return(Callable&)> call_table[] =
|
||||
{ impl::dispatch_helper<Callable, Return, wrapper<Types>>... };
|
||||
if (index < impl::length<Types...>::value) return call_table[index](c);
|
||||
throw std::out_of_range("Invalid index to fc::typelist::runtime::dispatch()");
|
||||
}
|
||||
template<typename List, typename Callable>
|
||||
auto dispatch(List l, int64_t index, Callable c) {
|
||||
if (index < 0) throw std::out_of_range("Negative index to fc::typelist::runtime::dispatch()");
|
||||
return dispatch(l, std::size_t(index), std::move(c));
|
||||
}
|
||||
|
||||
/// @brief Invoke the provided callable with an argument wrapper<Type>() for each type in the list
|
||||
template<typename... Types, typename Callable>
|
||||
void for_each(list<Types...>, Callable c) {
|
||||
bool trues[] = { [](Callable& c, auto t) { c(t); return true; }(c, wrapper<Types>())... };
|
||||
(void)(trues);
|
||||
}
|
||||
|
||||
} } } // namespace fc::typelist::runtime
|
||||
|
|
@ -112,6 +112,22 @@ namespace fc {
|
|||
return _name.c_str();
|
||||
}
|
||||
};
|
||||
template<typename T> struct get_typename< const T* >
|
||||
{
|
||||
static const char* name()
|
||||
{
|
||||
static std::string n = std::string("const ") + get_typename<T>::name() + "*";
|
||||
return n.c_str();
|
||||
}
|
||||
};
|
||||
template<typename T> struct get_typename< T* >
|
||||
{
|
||||
static const char* name()
|
||||
{
|
||||
static std::string n = std::string(get_typename<T>::name()) + "*";
|
||||
return n.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
struct unsigned_int;
|
||||
class variant_object;
|
||||
|
|
|
|||
|
|
@ -23,174 +23,6 @@ namespace fc {
|
|||
// Implementation details, the user should not import this:
|
||||
namespace impl {
|
||||
|
||||
template<int N, typename... Ts>
|
||||
struct storage_ops;
|
||||
|
||||
template<typename X, typename... Ts>
|
||||
struct position;
|
||||
|
||||
template<typename... Ts>
|
||||
struct type_info;
|
||||
|
||||
template<typename StaticVariant>
|
||||
struct copy_construct
|
||||
{
|
||||
typedef void result_type;
|
||||
StaticVariant& sv;
|
||||
copy_construct( StaticVariant& s ):sv(s){}
|
||||
template<typename T>
|
||||
void operator()( const T& v )const
|
||||
{
|
||||
sv.init(v);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename StaticVariant>
|
||||
struct move_construct
|
||||
{
|
||||
typedef void result_type;
|
||||
StaticVariant& sv;
|
||||
move_construct( StaticVariant& s ):sv(s){}
|
||||
template<typename T>
|
||||
void operator()( T& v )const
|
||||
{
|
||||
sv.init( std::move(v) );
|
||||
}
|
||||
};
|
||||
|
||||
template<int N, typename T, typename... Ts>
|
||||
struct storage_ops<N, T&, Ts...> {
|
||||
static void del(int n, void *data) {}
|
||||
static void con(int n, void *data) {}
|
||||
|
||||
template<typename visitor>
|
||||
static typename visitor::result_type apply(int n, void *data, visitor& v) {}
|
||||
|
||||
template<typename visitor>
|
||||
static typename visitor::result_type apply(int n, void *data, const visitor& v) {}
|
||||
|
||||
template<typename visitor>
|
||||
static typename visitor::result_type apply(int n, const void *data, visitor& v) {}
|
||||
|
||||
template<typename visitor>
|
||||
static typename visitor::result_type apply(int n, const void *data, const visitor& v) {}
|
||||
};
|
||||
|
||||
template<int N, typename T, typename... Ts>
|
||||
struct storage_ops<N, T, Ts...> {
|
||||
static void del(int n, void *data) {
|
||||
if(n == N) reinterpret_cast<T*>(data)->~T();
|
||||
else storage_ops<N + 1, Ts...>::del(n, data);
|
||||
}
|
||||
static void con(int n, void *data) {
|
||||
if(n == N) new(reinterpret_cast<T*>(data)) T();
|
||||
else storage_ops<N + 1, Ts...>::con(n, data);
|
||||
}
|
||||
|
||||
template<typename visitor>
|
||||
static typename visitor::result_type apply(int n, void *data, visitor& v) {
|
||||
if(n == N) return v(*reinterpret_cast<T*>(data));
|
||||
else return storage_ops<N + 1, Ts...>::apply(n, data, v);
|
||||
}
|
||||
|
||||
template<typename visitor>
|
||||
static typename visitor::result_type apply(int n, void *data, const visitor& v) {
|
||||
if(n == N) return v(*reinterpret_cast<T*>(data));
|
||||
else return storage_ops<N + 1, Ts...>::apply(n, data, v);
|
||||
}
|
||||
|
||||
template<typename visitor>
|
||||
static typename visitor::result_type apply(int n, const void *data, visitor& v) {
|
||||
if(n == N) return v(*reinterpret_cast<const T*>(data));
|
||||
else return storage_ops<N + 1, Ts...>::apply(n, data, v);
|
||||
}
|
||||
|
||||
template<typename visitor>
|
||||
static typename visitor::result_type apply(int n, const void *data, const visitor& v) {
|
||||
if(n == N) return v(*reinterpret_cast<const T*>(data));
|
||||
else return storage_ops<N + 1, Ts...>::apply(n, data, v);
|
||||
}
|
||||
};
|
||||
|
||||
template<int N>
|
||||
struct storage_ops<N> {
|
||||
static void del(int n, void *data) {
|
||||
FC_THROW_EXCEPTION( fc::assert_exception, "Internal error: static_variant tag is invalid.");
|
||||
}
|
||||
static void con(int n, void *data) {
|
||||
FC_THROW_EXCEPTION( fc::assert_exception, "Internal error: static_variant tag is invalid." );
|
||||
}
|
||||
|
||||
template<typename visitor>
|
||||
static typename visitor::result_type apply(int n, void *data, visitor& v) {
|
||||
FC_THROW_EXCEPTION( fc::assert_exception, "Internal error: static_variant tag is invalid." );
|
||||
}
|
||||
template<typename visitor>
|
||||
static typename visitor::result_type apply(int n, void *data, const visitor& v) {
|
||||
FC_THROW_EXCEPTION( fc::assert_exception, "Internal error: static_variant tag is invalid." );
|
||||
}
|
||||
template<typename visitor>
|
||||
static typename visitor::result_type apply(int n, const void *data, visitor& v) {
|
||||
FC_THROW_EXCEPTION( fc::assert_exception, "Internal error: static_variant tag is invalid." );
|
||||
}
|
||||
template<typename visitor>
|
||||
static typename visitor::result_type apply(int n, const void *data, const visitor& v) {
|
||||
FC_THROW_EXCEPTION( fc::assert_exception, "Internal error: static_variant tag is invalid." );
|
||||
}
|
||||
};
|
||||
|
||||
template<typename X>
|
||||
struct position<X> {
|
||||
static constexpr int pos = -1;
|
||||
};
|
||||
|
||||
template<typename X, typename... Ts>
|
||||
struct position<X, X, Ts...> {
|
||||
static constexpr int pos = 0;
|
||||
};
|
||||
|
||||
template<typename X, typename T, typename... Ts>
|
||||
struct position<X, T, Ts...> {
|
||||
static constexpr int pos = position<X, Ts...>::pos != -1 ? position<X, Ts...>::pos + 1 : -1;
|
||||
};
|
||||
|
||||
template<typename T, typename... Ts>
|
||||
struct type_info<T&, Ts...> {
|
||||
static constexpr bool no_reference_types = false;
|
||||
static constexpr bool no_duplicates = position<T, Ts...>::pos == -1 && type_info<Ts...>::no_duplicates;
|
||||
static constexpr size_t size = type_info<Ts...>::size > sizeof(T&) ? type_info<Ts...>::size : sizeof(T&);
|
||||
static constexpr size_t count = 1 + type_info<Ts...>::count;
|
||||
};
|
||||
|
||||
template<typename T, typename... Ts>
|
||||
struct type_info<T, Ts...> {
|
||||
static constexpr bool no_reference_types = type_info<Ts...>::no_reference_types;
|
||||
static constexpr bool no_duplicates = position<T, Ts...>::pos == -1 && type_info<Ts...>::no_duplicates;
|
||||
static constexpr size_t size = type_info<Ts...>::size > sizeof(T) ? type_info<Ts...>::size : sizeof(T&);
|
||||
static constexpr size_t count = 1 + type_info<Ts...>::count;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct type_info<> {
|
||||
static constexpr bool no_reference_types = true;
|
||||
static constexpr bool no_duplicates = true;
|
||||
static constexpr size_t count = 0;
|
||||
static constexpr size_t size = 0;
|
||||
};
|
||||
|
||||
template<typename TTag>
|
||||
constexpr size_t size( TTag )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename TTag, typename A, typename...Ts>
|
||||
constexpr size_t size( TTag tag )
|
||||
{
|
||||
return tag <= 0 ? sizeof(A) : size<TTag, Ts...>( --tag );
|
||||
}
|
||||
|
||||
|
||||
class dynamic_storage
|
||||
{
|
||||
char* storage;
|
||||
|
|
@ -206,71 +38,36 @@ public:
|
|||
void release();
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace impl
|
||||
|
||||
template<int L,typename Visitor,typename Data>
|
||||
static const std::array<typename Visitor::result_type(*)(Visitor&,Data),L>
|
||||
init_wrappers( Visitor& v, Data d, typename Visitor::result_type(**funcs)(Visitor&,Data) = 0)
|
||||
{
|
||||
return std::array<typename Visitor::result_type(*)(Visitor&,Data),L>();
|
||||
}
|
||||
|
||||
template<int L,typename Visitor,typename Data,typename T, typename ... Types>
|
||||
static const std::array<typename Visitor::result_type(*)(Visitor&,Data),L>
|
||||
init_wrappers( Visitor& v, Data d, typename Visitor::result_type(**funcs)(Visitor&,Data) = 0 )
|
||||
{
|
||||
std::array<typename Visitor::result_type(*)(Visitor&,Data),L> result{};
|
||||
if( !funcs ) funcs = result.data();
|
||||
*funcs++ = [] ( Visitor& v, Data d ) { return v( *reinterpret_cast<T*>( d ) ); };
|
||||
init_wrappers<L,Visitor,Data,Types...>( v, d, funcs );
|
||||
return result;
|
||||
}
|
||||
|
||||
template<int L,typename Visitor,typename Data>
|
||||
static const std::array<typename Visitor::result_type(*)(Visitor&,Data),L>
|
||||
init_const_wrappers( Visitor& v, Data d, typename Visitor::result_type(**funcs)(Visitor&,Data) = 0 )
|
||||
{
|
||||
return std::array<typename Visitor::result_type(*)(Visitor&,Data),L>();
|
||||
}
|
||||
|
||||
template<int L,typename Visitor,typename Data,typename T, typename ... Types>
|
||||
static const std::array<typename Visitor::result_type(*)(Visitor&,Data),L>
|
||||
init_const_wrappers( Visitor& v, Data d, typename Visitor::result_type(**funcs)(Visitor&,Data) = 0 )
|
||||
{
|
||||
std::array<typename Visitor::result_type(*)(Visitor&,Data),L> result{};
|
||||
if( !funcs ) funcs = result.data();
|
||||
*funcs++ = [] ( Visitor& v, Data d ) { return v( *reinterpret_cast<const T*>( d ) ); };
|
||||
init_const_wrappers<L,Visitor,Data,Types...>( v, d, funcs );
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename... Types>
|
||||
class static_variant {
|
||||
public:
|
||||
using tag_type = int64_t;
|
||||
using list = typelist::list<Types...>;
|
||||
|
||||
protected:
|
||||
static_assert(impl::type_info<Types...>::no_reference_types, "Reference types are not permitted in static_variant.");
|
||||
static_assert(impl::type_info<Types...>::no_duplicates, "static_variant type arguments contain duplicate types.");
|
||||
static_assert(typelist::length<typelist::filter<list, std::is_reference>>() == 0,
|
||||
"Reference types are not permitted in static_variant.");
|
||||
static_assert(typelist::length<typelist::concat_unique<list>>() == typelist::length<list>(),
|
||||
"static_variant type arguments contain duplicate types.");
|
||||
|
||||
template<typename X>
|
||||
using type_in_typelist = typename std::enable_if<impl::position<X, Types...>::pos != -1, X>::type; // type is in typelist of static_variant.
|
||||
using type_in_typelist = std::enable_if_t<typelist::index_of<list, X>() != -1>;
|
||||
|
||||
tag_type _tag;
|
||||
impl::dynamic_storage storage;
|
||||
|
||||
template<typename X, typename = type_in_typelist<X>>
|
||||
void init(const X& x) {
|
||||
_tag = impl::position<X, Types...>::pos;
|
||||
_tag = typelist::index_of<list, X>();
|
||||
storage.alloc( sizeof(X) );
|
||||
new(storage.data()) X(x);
|
||||
}
|
||||
|
||||
template<typename X, typename = type_in_typelist<X>>
|
||||
void init(X&& x) {
|
||||
_tag = impl::position<X, Types...>::pos;
|
||||
_tag = typelist::index_of<list, X>();
|
||||
storage.alloc( sizeof(X) );
|
||||
new(storage.data()) X( std::move(x) );
|
||||
}
|
||||
|
|
@ -280,26 +77,48 @@ protected:
|
|||
FC_ASSERT( tag >= 0 );
|
||||
FC_ASSERT( tag < count() );
|
||||
_tag = tag;
|
||||
storage.alloc( impl::size<tag_type, Types...>( tag ) );
|
||||
impl::storage_ops<0, Types...>::con(_tag, storage.data());
|
||||
typelist::runtime::dispatch(list(), tag, [this](auto t) {
|
||||
using T = typename decltype(t)::type;
|
||||
storage.alloc(sizeof(T));
|
||||
new(reinterpret_cast<T*>(storage.data())) T();
|
||||
});
|
||||
}
|
||||
|
||||
void clean()
|
||||
{
|
||||
impl::storage_ops<0, Types...>::del(_tag, storage.data() );
|
||||
typelist::runtime::dispatch(list(), _tag, [data=storage.data()](auto t) {
|
||||
using T = typename decltype(t)::type;
|
||||
reinterpret_cast<T*>(data)->~T();
|
||||
});
|
||||
storage.release();
|
||||
}
|
||||
|
||||
template<typename T, typename = void>
|
||||
struct import_helper {
|
||||
static static_variant construct(const T&) {
|
||||
FC_THROW_EXCEPTION(assert_exception, "Cannot import unsupported type ${T} into static_variant",
|
||||
("T", get_typename<T>::name()));
|
||||
}
|
||||
static static_variant construct(T&&) {
|
||||
FC_THROW_EXCEPTION(assert_exception, "Cannot import unsupported type ${T} into static_variant",
|
||||
("T", get_typename<T>::name()));
|
||||
}
|
||||
};
|
||||
template<typename T>
|
||||
struct import_helper<T, type_in_typelist<T>> {
|
||||
static static_variant construct(const T& t) {
|
||||
return static_variant(t);
|
||||
}
|
||||
static static_variant construct(T&& t) {
|
||||
return static_variant(std::move(t));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename StaticVariant>
|
||||
friend struct impl::copy_construct;
|
||||
template<typename StaticVariant>
|
||||
friend struct impl::move_construct;
|
||||
public:
|
||||
template<typename X, typename = type_in_typelist<X>>
|
||||
struct tag
|
||||
{
|
||||
static constexpr int value = impl::position<X, Types...>::pos;
|
||||
static constexpr int value = typelist::index_of<list, X>();
|
||||
};
|
||||
|
||||
struct type_lt {
|
||||
|
|
@ -310,6 +129,25 @@ public:
|
|||
};
|
||||
using flat_set_type = flat_set<static_variant, type_lt>;
|
||||
|
||||
/// Import the value from a foreign static_variant with types not in this one, and throw if the value is an
|
||||
/// incompatible type
|
||||
template<typename... Other>
|
||||
static static_variant import_from(const static_variant<Other...>& other) {
|
||||
return typelist::runtime::dispatch(typelist::list<Other...>(), other.which(), [&other](auto t) {
|
||||
using other_type = typename decltype(t)::type;
|
||||
return import_helper<other_type>::construct(other.template get<other_type>());
|
||||
});
|
||||
}
|
||||
/// Import the value from a foreign static_variant with types not in this one, and throw if the value is an
|
||||
/// incompatible type
|
||||
template<typename... Other>
|
||||
static static_variant import_from(static_variant<Other...>&& other) {
|
||||
return typelist::runtime::dispatch(typelist::list<Other...>(), other.which(), [&other](auto t) {
|
||||
using other_type = typename decltype(t)::type;
|
||||
return import_helper<other_type>::construct(std::move(other.template get<other_type>()));
|
||||
});
|
||||
}
|
||||
|
||||
static_variant()
|
||||
{
|
||||
init_from_tag(0);
|
||||
|
|
@ -318,23 +156,41 @@ public:
|
|||
template<typename... Other>
|
||||
static_variant( const static_variant<Other...>& cpy )
|
||||
{
|
||||
cpy.visit( impl::copy_construct<static_variant>(*this) );
|
||||
typelist::runtime::dispatch(typelist::list<Other...>(), cpy.which(), [this, &cpy](auto t) mutable {
|
||||
this->init(cpy.template get<typename decltype(t)::type>());
|
||||
});
|
||||
}
|
||||
|
||||
static_variant( const static_variant& cpy )
|
||||
{
|
||||
cpy.visit( impl::copy_construct<static_variant>(*this) );
|
||||
typelist::runtime::dispatch(list(), cpy.which(), [this, &cpy](auto t) mutable {
|
||||
this->init(cpy.template get<typename decltype(t)::type>());
|
||||
});
|
||||
}
|
||||
|
||||
static_variant( static_variant&& mv )
|
||||
{
|
||||
mv.visit( impl::move_construct<static_variant>(*this) );
|
||||
typelist::runtime::dispatch(list(), mv.which(), [this, &mv](auto t) mutable {
|
||||
this->init(std::move(mv.template get<typename decltype(t)::type>()));
|
||||
});
|
||||
}
|
||||
|
||||
template<typename... Other>
|
||||
static_variant( static_variant<Other...>&& mv )
|
||||
{
|
||||
typelist::runtime::dispatch(typelist::list<Other...>(), mv.which(), [this, &mv](auto t) mutable {
|
||||
this->init(std::move(mv.template get<typename decltype(t)::type>()));
|
||||
});
|
||||
}
|
||||
|
||||
template<typename X, typename = type_in_typelist<X>>
|
||||
static_variant(const X& v) {
|
||||
init(v);
|
||||
}
|
||||
template<typename X, typename = type_in_typelist<X>>
|
||||
static_variant(X&& v) {
|
||||
init(std::move(v));
|
||||
}
|
||||
|
||||
~static_variant() {
|
||||
clean();
|
||||
|
|
@ -350,20 +206,32 @@ public:
|
|||
{
|
||||
if( this == &v ) return *this;
|
||||
clean();
|
||||
v.visit( impl::copy_construct<static_variant>(*this) );
|
||||
typelist::runtime::dispatch(list(), v.which(), [this, &v](auto t)mutable {
|
||||
this->init(v.template get<typename decltype(t)::type>());
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
static_variant& operator=( static_variant&& v )
|
||||
{
|
||||
if( this == &v ) return *this;
|
||||
clean();
|
||||
v.visit( impl::move_construct<static_variant>(*this) );
|
||||
typelist::runtime::dispatch(list(), v.which(), [this, &v](auto t)mutable {
|
||||
this->init(std::move(v.template get<typename decltype(t)::type>()));
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend bool operator==( const static_variant& a, const static_variant& b ) {
|
||||
if (a.which() != b.which())
|
||||
return false;
|
||||
return typelist::runtime::dispatch(list(), a.which(), [&a, &b](auto t) {
|
||||
return a.get<typename decltype(t)::type>() == b.get<typename decltype(t)::type>();
|
||||
});
|
||||
}
|
||||
|
||||
template<typename X, typename = type_in_typelist<X>>
|
||||
X& get() {
|
||||
if(_tag == impl::position<X, Types...>::pos) {
|
||||
if(_tag == typelist::index_of<list, X>()) {
|
||||
return *reinterpret_cast<X*>(storage.data());
|
||||
} else {
|
||||
FC_THROW_EXCEPTION( fc::assert_exception, "static_variant does not contain a value of type ${t}", ("t",fc::get_typename<X>::name()) );
|
||||
|
|
@ -371,7 +239,7 @@ public:
|
|||
}
|
||||
template<typename X, typename = type_in_typelist<X>>
|
||||
const X& get() const {
|
||||
if(_tag == impl::position<X, Types...>::pos) {
|
||||
if(_tag == typelist::index_of<list, X>()) {
|
||||
return *reinterpret_cast<const X*>(storage.data());
|
||||
} else {
|
||||
FC_THROW_EXCEPTION( fc::assert_exception, "static_variant does not contain a value of type ${t}", ("t",fc::get_typename<X>::name()) );
|
||||
|
|
@ -400,36 +268,40 @@ public:
|
|||
template<typename visitor>
|
||||
static typename visitor::result_type visit( tag_type tag, visitor& v, void* data )
|
||||
{
|
||||
static const auto wrappers = init_wrappers<impl::type_info<Types...>::count,visitor,void*,Types...>( v, data );
|
||||
FC_ASSERT( tag >= 0 && tag < count(), "Unsupported type ${tag}!", ("tag",tag) );
|
||||
return wrappers[tag]( v, data );
|
||||
return typelist::runtime::dispatch(list(), tag, [&v, data](auto t) {
|
||||
return v(*reinterpret_cast<typename decltype(t)::type*>(data));
|
||||
});
|
||||
}
|
||||
|
||||
template<typename visitor>
|
||||
static typename visitor::result_type visit( tag_type tag, const visitor& v, void* data )
|
||||
{
|
||||
static const auto wrappers = init_wrappers<impl::type_info<Types...>::count,const visitor,void*,Types...>( v, data );
|
||||
FC_ASSERT( tag >= 0 && tag < count(), "Unsupported type ${tag}!", ("tag",tag) );
|
||||
return wrappers[tag]( v, data );
|
||||
return typelist::runtime::dispatch(list(), tag, [&v, data](auto t) {
|
||||
return v(*reinterpret_cast<typename decltype(t)::type*>(data));
|
||||
});
|
||||
}
|
||||
|
||||
template<typename visitor>
|
||||
static typename visitor::result_type visit( tag_type tag, visitor& v, const void* data )
|
||||
{
|
||||
static const auto wrappers = init_const_wrappers<impl::type_info<Types...>::count,visitor,const void*,Types...>( v, data );
|
||||
FC_ASSERT( tag >= 0 && tag < count(), "Unsupported type ${tag}!", ("tag",tag) );
|
||||
return wrappers[tag]( v, data );
|
||||
return typelist::runtime::dispatch(list(), tag, [&v, data](auto t) {
|
||||
return v(*reinterpret_cast<const typename decltype(t)::type*>(data));
|
||||
});
|
||||
}
|
||||
|
||||
template<typename visitor>
|
||||
static typename visitor::result_type visit( tag_type tag, const visitor& v, const void* data )
|
||||
{
|
||||
static const auto wrappers = init_const_wrappers<impl::type_info<Types...>::count,const visitor,const void*,Types...>( v, data );
|
||||
FC_ASSERT( tag >= 0 && tag < count(), "Unsupported type ${tag}!", ("tag",tag) );
|
||||
return wrappers[tag]( v, data );
|
||||
return typelist::runtime::dispatch(list(), tag, [&v, data](auto t) {
|
||||
return v(*reinterpret_cast<const typename decltype(t)::type*>(data));
|
||||
});
|
||||
}
|
||||
|
||||
static constexpr int count() { return impl::type_info<Types...>::count; }
|
||||
static constexpr int count() { return typelist::length<list>(); }
|
||||
void set_which( tag_type w ) {
|
||||
FC_ASSERT( w >= 0 );
|
||||
FC_ASSERT( w < count() );
|
||||
|
|
@ -442,6 +314,12 @@ public:
|
|||
template<typename T>
|
||||
bool is_type() const { return _tag == tag<T>::value; }
|
||||
};
|
||||
template<> class static_variant<> {
|
||||
public:
|
||||
using tag_type = int64_t;
|
||||
static_variant() { FC_THROW_EXCEPTION(assert_exception, "Cannot create static_variant with no types"); }
|
||||
};
|
||||
template<typename... Types> class static_variant<typelist::list<Types...>> : public static_variant<Types...> {};
|
||||
|
||||
struct from_static_variant
|
||||
{
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ add_executable( all_tests all_tests.cpp
|
|||
thread/thread_tests.cpp
|
||||
thread/parallel_tests.cpp
|
||||
bloom_test.cpp
|
||||
reflection_tests.cpp
|
||||
serialization_test.cpp
|
||||
stacktrace_test.cpp
|
||||
time_test.cpp
|
||||
|
|
|
|||
126
tests/reflection_tests.cpp
Normal file
126
tests/reflection_tests.cpp
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <fc/exception/exception.hpp>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
struct reflect_test_base {
|
||||
int x = 1;
|
||||
char y = 'a';
|
||||
};
|
||||
struct reflect_test_derived : reflect_test_base {
|
||||
double z = 3.14;
|
||||
};
|
||||
struct reflect_layer_1 { reflect_test_base b; int32_t n; };
|
||||
struct reflect_layer_2 { reflect_layer_1 l1; reflect_test_derived d; };
|
||||
struct reflect_layer_3 { reflect_layer_2 l2; int32_t i; };
|
||||
|
||||
FC_REFLECT( reflect_test_base, (x)(y) );
|
||||
FC_REFLECT_DERIVED( reflect_test_derived, (reflect_test_base), (z) );
|
||||
FC_REFLECT( reflect_layer_1, (b)(n) );
|
||||
FC_REFLECT( reflect_layer_2, (l1)(d) );
|
||||
FC_REFLECT( reflect_layer_3, (l2)(i) );
|
||||
|
||||
BOOST_AUTO_TEST_SUITE( fc_reflection )
|
||||
|
||||
BOOST_AUTO_TEST_CASE( reflection_static_tests )
|
||||
{
|
||||
// These are all compile-time tests, nothing actually happens here at runtime
|
||||
using base_reflection = fc::reflector<reflect_test_base>;
|
||||
using derived_reflection = fc::reflector<reflect_test_derived>;
|
||||
static_assert(fc::typelist::length<base_reflection::members>() == 2, "");
|
||||
static_assert(fc::typelist::length<derived_reflection::members>() == 3, "");
|
||||
static_assert(fc::typelist::at<derived_reflection::members, 0>::is_derived, "");
|
||||
static_assert(std::is_same<fc::typelist::at<derived_reflection::members, 0>::field_container,
|
||||
reflect_test_base>::value, "");
|
||||
static_assert(fc::typelist::at<derived_reflection::members, 1>::is_derived, "");
|
||||
static_assert(std::is_same<fc::typelist::at<derived_reflection::members, 1>::field_container,
|
||||
reflect_test_base>::value, "");
|
||||
static_assert(fc::typelist::at<derived_reflection::members, 2>::is_derived == false, "");
|
||||
static_assert(std::is_same<fc::typelist::slice<fc::typelist::list<int, bool, char>, 0, 1>,
|
||||
fc::typelist::list<int>>::value, "");
|
||||
static_assert(std::is_same<fc::typelist::slice<fc::typelist::list<int, bool, char>, 0, 2>,
|
||||
fc::typelist::list<int, bool>>::value, "");
|
||||
static_assert(std::is_same<fc::typelist::slice<fc::typelist::list<int, bool, char>, 0, 3>,
|
||||
fc::typelist::list<int, bool, char>>::value, "");
|
||||
static_assert(std::is_same<fc::typelist::slice<fc::typelist::list<int, bool, char>, 1, 3>,
|
||||
fc::typelist::list<bool, char>>::value, "");
|
||||
static_assert(std::is_same<fc::typelist::slice<fc::typelist::list<int, bool, char>, 2, 3>,
|
||||
fc::typelist::list<char>>::value, "");
|
||||
static_assert(std::is_same<fc::typelist::slice<fc::typelist::list<int, bool, char>, 1, 2>,
|
||||
fc::typelist::list<bool>>::value, "");
|
||||
static_assert(std::is_same<fc::typelist::slice<fc::typelist::list<int, bool, char>, 1>,
|
||||
fc::typelist::list<bool, char>>::value, "");
|
||||
static_assert(std::is_same<fc::typelist::make_sequence<0>, fc::typelist::list<>>::value, "");
|
||||
static_assert(std::is_same<fc::typelist::make_sequence<1>,
|
||||
fc::typelist::list<std::integral_constant<size_t, 0>>>::value, "");
|
||||
static_assert(std::is_same<fc::typelist::make_sequence<2>,
|
||||
fc::typelist::list<std::integral_constant<size_t, 0>,
|
||||
std::integral_constant<size_t, 1>>>::value, "");
|
||||
static_assert(std::is_same<fc::typelist::make_sequence<3>,
|
||||
fc::typelist::list<std::integral_constant<size_t, 0>,
|
||||
std::integral_constant<size_t, 1>,
|
||||
std::integral_constant<size_t, 2>>>::value, "");
|
||||
static_assert(std::is_same<fc::typelist::zip<fc::typelist::list<>, fc::typelist::list<>>,
|
||||
fc::typelist::list<>>::value, "");
|
||||
static_assert(std::is_same<fc::typelist::zip<fc::typelist::list<bool>, fc::typelist::list<char>>,
|
||||
fc::typelist::list<fc::typelist::list<bool, char>>>::value, "");
|
||||
static_assert(std::is_same<fc::typelist::zip<fc::typelist::list<int, bool>, fc::typelist::list<char, double>>,
|
||||
fc::typelist::list<fc::typelist::list<int, char>,
|
||||
fc::typelist::list<bool, double>>>::value, "");
|
||||
static_assert(std::is_same<fc::typelist::index<fc::typelist::list<>>, fc::typelist::list<>>::value, "");
|
||||
static_assert(std::is_same<fc::typelist::index<fc::typelist::list<int, bool, char, double>>,
|
||||
fc::typelist::list<fc::typelist::list<std::integral_constant<size_t, 0>, int>,
|
||||
fc::typelist::list<std::integral_constant<size_t, 1>, bool>,
|
||||
fc::typelist::list<std::integral_constant<size_t, 2>, char>,
|
||||
fc::typelist::list<std::integral_constant<size_t, 3>, double>>
|
||||
>::value, "");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( typelist_dispatch_test )
|
||||
{
|
||||
using list = fc::typelist::list<float, bool, char>;
|
||||
auto get_name = [](auto t) -> std::string { return fc::get_typename<typename decltype(t)::type>::name(); };
|
||||
BOOST_CHECK_EQUAL(fc::typelist::runtime::dispatch(list(), 0ul, get_name), "float");
|
||||
BOOST_CHECK_EQUAL(fc::typelist::runtime::dispatch(list(), 1ul, get_name), "bool");
|
||||
BOOST_CHECK_EQUAL(fc::typelist::runtime::dispatch(list(), 2ul, get_name), "char");
|
||||
}
|
||||
|
||||
// Helper template to use fc::typelist::at without a comma, for macro friendliness
|
||||
template<typename T> struct index_from { template<std::size_t idx> using at = fc::typelist::at<T, idx>; };
|
||||
BOOST_AUTO_TEST_CASE( reflection_get_test )
|
||||
{ try {
|
||||
reflect_test_derived derived;
|
||||
reflect_test_base& base = derived;
|
||||
|
||||
using base_reflector = fc::reflector<reflect_test_base>;
|
||||
using derived_reflector = fc::reflector<reflect_test_derived>;
|
||||
|
||||
BOOST_CHECK(index_from<base_reflector::members>::at<0>::get(base) == 1);
|
||||
BOOST_CHECK(index_from<base_reflector::members>::at<1>::get(base) == 'a');
|
||||
|
||||
fc::typelist::at<base_reflector::members, 0>::get(base) = 5;
|
||||
fc::typelist::at<base_reflector::members, 1>::get(base) = 'q';
|
||||
|
||||
BOOST_CHECK(index_from<base_reflector::members>::at<0>::get(base) == 5);
|
||||
BOOST_CHECK(index_from<base_reflector::members>::at<1>::get(base) == 'q');
|
||||
|
||||
BOOST_CHECK(index_from<derived_reflector::members>::at<0>::get(derived) == 5);
|
||||
BOOST_CHECK(index_from<derived_reflector::members>::at<1>::get(derived) == 'q');
|
||||
BOOST_CHECK(index_from<derived_reflector::members>::at<2>::get(derived) == 3.14);
|
||||
|
||||
fc::typelist::at<derived_reflector::members, 1>::get(derived) = 'X';
|
||||
|
||||
BOOST_CHECK(index_from<base_reflector::members>::at<1>::get(base) == 'X');
|
||||
|
||||
reflect_layer_3 l3;
|
||||
BOOST_CHECK(index_from<index_from<index_from<index_from<fc::reflector<reflect_layer_3>::members>::at<0>
|
||||
::reflector::members>::at<0>::reflector::members>::at<0>::reflector::members>::at<1>::get(l3.l2.l1.b)
|
||||
== 'a');
|
||||
BOOST_CHECK(index_from<index_from<index_from<fc::reflector<reflect_layer_3>::members>::at<0>::reflector::members>
|
||||
::at<1>::reflector::members>::at<1>::get(l3.l2.d) == 'a');
|
||||
BOOST_CHECK(index_from<index_from<index_from<fc::reflector<reflect_layer_3>::members>::at<0>::reflector::members>
|
||||
::at<1>::reflector::members>::at<2>::get(l3.l2.d) == 3.14);
|
||||
} FC_CAPTURE_LOG_AND_RETHROW( (0) ) }
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
|
@ -35,7 +35,6 @@ namespace fc { namespace test {
|
|||
inline bool operator < ( const item& a, const item& b )
|
||||
{ return ( std::tie( a.level, a.w ) < std::tie( b.level, b.w ) ); }
|
||||
|
||||
|
||||
} } // namespace fc::test
|
||||
|
||||
FC_REFLECT( fc::test::item_wrapper, (v) );
|
||||
|
|
|
|||
|
|
@ -182,10 +182,7 @@ BOOST_AUTO_TEST_CASE( nested_objects_test )
|
|||
|
||||
from_variant( v, sv1, nested_levels + 2 );
|
||||
|
||||
auto sv_equal = [](const fc::static_variant<fc::test::item>& v1, const fc::static_variant<fc::test::item>& v2) {
|
||||
return v1.get<fc::test::item>() == v2.get<fc::test::item>();
|
||||
};
|
||||
BOOST_CHECK( sv_equal(sv, sv1) );
|
||||
BOOST_CHECK( sv == sv1 );
|
||||
|
||||
// both log and dump should never throw
|
||||
BOOST_TEST_MESSAGE( "========== About to log static_variant. ==========" );
|
||||
|
|
@ -218,7 +215,7 @@ BOOST_AUTO_TEST_CASE( nested_objects_test )
|
|||
|
||||
from_variant( v, vec1, nested_levels + 3 );
|
||||
|
||||
BOOST_CHECK( std::equal(vec.begin(), vec.end(), vec1.begin(), sv_equal) );
|
||||
BOOST_CHECK( vec == vec1 );
|
||||
|
||||
// both log and dump should never throw
|
||||
BOOST_TEST_MESSAGE( "========== About to log vector. ==========" );
|
||||
|
|
|
|||
Loading…
Reference in a new issue