diff --git a/include/fc/reflect/reflect.hpp b/include/fc/reflect/reflect.hpp index 7578360..348477a 100644 --- a/include/fc/reflect/reflect.hpp +++ b/include/fc/reflect/reflect.hpp @@ -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 #include +#include namespace fc { +template struct reflector; +namespace member_names { +/// A template which stores the name of the native member at a given index in a given class +template 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 +struct field_reflection { + using container = Container; + using type = Member; + using reflector = fc::reflector; + 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::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 +struct inherited_field_reflection { + using container = Derived; + using field_container = Base; + using type = Member; + using reflector = fc::reflector; + 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::native_members::template at; + return Reflector::get_name(); + } +}; + +namespace impl { +/// Helper template to create a @ref field_reflection without any commas (makes it macro-friendly) +template +struct Reflect_type { + template + struct with_field_type { + template + struct at_index { + template + struct with_field_pointer { + using type = field_reflection; + }; + }; + }; +}; +/// Template to make a transformer of a @ref field_reflection from a base class to a derived class +template +struct Derivation_reflection_transformer { + template struct transform; + template + struct transform> { + using type = inherited_field_reflection; + }; + template + struct transform> { + using type = inherited_field_reflection; + }; +}; +} // 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::members, impl::Derivation_reflection_transformer>> +/// Macro to concatenate a new @ref field_reflection to a typelist +#define FC_CONCAT_MEMBER_REFLECTION(r, container, idx, member) \ + ::add::template with_field_type \ + ::template at_index \ + ::template with_field_pointer<&container::member>::type> +#define FC_REFLECT_MEMBER_NAME(r, container, idx, member) \ + template<> struct member_name { constexpr static const char* value = BOOST_PP_STRINGIZE(member); }; +#define FC_REFLECT_TEMPLATE_MEMBER_NAME(r, data, idx, member) \ + template struct member_name { \ + 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 + /** * @brief defines visit functions for T * Unless this is specialized, visit() will not be defined for T. @@ -34,6 +139,14 @@ template 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::total_member_count - -#define FC_REFLECT_MEMBER_COUNT( r, OP, elem ) \ - OP 1 - #define FC_REFLECT_DERIVED_IMPL_INLINE( TYPE, INHERITS, MEMBERS ) \ template\ 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\ -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\ -void fc::reflector::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 {\ 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::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(), \ + total_member_count = typelist::length() \ }; \ 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 struct get_typename { static const char* name() { return BOOST_PP_STRINGIZE(TYPE); } }; \ + template struct get_typename { \ + static const char* name() { return BOOST_PP_STRINGIZE(TYPE); } \ + }; \ template struct reflector {\ 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::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(), \ + total_member_count = typelist::length() \ }; \ 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 struct reflector {\ * @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,11 @@ namespace fc { \ template<> struct get_typename { static const char* name() { return BOOST_PP_STRINGIZE(TYPE); } }; \ } -#define FC_REFLECT_FWD( TYPE ) \ -namespace fc { \ - template<> struct get_typename { static const char* name() { return BOOST_PP_STRINGIZE(TYPE); } }; \ -template<> struct reflector {\ - 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 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 ) - +// vvv These are preserved to merge more smoothly vvv +#define FC_REFLECT_BASE_MEMBER_COUNT( r, OP, elem ) \ + OP fc::reflector::total_member_count +#define FC_REFLECT_MEMBER_COUNT( r, OP, elem ) \ + OP 1 +// ^^^ These can be removed after updating graphene/protocol/types.hpp ^^^ diff --git a/include/fc/reflect/typelist.hpp b/include/fc/reflect/typelist.hpp new file mode 100644 index 0000000..d1d3e0d --- /dev/null +++ b/include/fc/reflect/typelist.hpp @@ -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 +#include + +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 struct list; + +namespace impl { +using typelist::list; + +template class> struct apply; +template class Delegate> +struct apply, Delegate> { using type = Delegate; }; + +template +struct length; +template<> struct length<> { constexpr static std::size_t value = 0; }; +template +struct length { constexpr static std::size_t value = length::value+1; }; + +template struct concat; +template +struct concat, list> { + using type = list; +}; +template +struct concat, list, NextList, Lists...> { + using type = typename concat, NextList, Lists...>::type; +}; + +template struct make_sequence; +template<> struct make_sequence<0> { using type = list<>; }; +template<> struct make_sequence<1> { using type = list>; }; +template +struct make_sequence { + using type = typename concat::type, + list>>::type; +}; + +template struct transform; +template +struct transform, Transformer> { + using type = list::type...>; +}; + +template struct index_of; +template struct index_of> { constexpr static int value = -1; }; +template +struct index_of> { + constexpr static int deeper = index_of>::value; + constexpr static int value = std::is_same::value? 0 : (deeper == -1? -1 : deeper + 1); +}; + +template struct concat_unique; +template +struct concat_unique, list<>> { + using type = list; +}; +template +struct concat_unique, list> { + using type = std::conditional_t>::value >= 0, + list, list>; +}; +template +struct concat_unique, list> { + using type = typename concat_unique< + typename concat_unique, list>::type, list>::type; +}; +template +struct concat_unique, list<>, Lists...> { + using type = typename concat_unique, Lists...>::type; +}; +template +struct concat_unique, L2, Lists...> { + using type = typename concat_unique>::type, L2, Lists...>::type; +}; + +template struct at; +template +struct at, 0> { using type = T; }; +template +struct at, index> : at, index-1> {}; + +template struct remove_at; +template +struct remove_at, list, 0> { using type = list; }; +template +struct remove_at, list, index> { + using type = typename remove_at, list, index-1>::type; +}; + +template class Filter, typename Filtered, typename List> struct filter; +template class Filter, typename... Filtered> +struct filter, list<>> { using type = list; }; +template class Filter, typename... Filtered, typename T1, typename... Types> +struct filter, list> { + using type = typename std::conditional_t::value, + filter, list>, + filter, list>>::type; +}; + +template struct slice; +template +struct slice, list, index, index, void> { using type = list; }; +template +struct slice, list, 0, end, std::enable_if_t> + : slice, list, 0, end-1> {}; +template +struct slice, list, start, end, std::enable_if_t> + : slice, list, start-1, end-1> {}; + +template struct zip; +template<> +struct zip, list<>> { using type = list<>; }; +template +struct zip, list> { + using type = typename concat>, typename zip, list>::type>::type; +}; + +template +Ret dispatch_helper(Callable& c) { return c(T()); } + +} // namespace impl + +/// The actual list type +template +struct list { using type = list; }; + +/// Apply a list of types as arguments to another template +template class Delegate> +using apply = typename impl::apply::type; + +/// Get the number of types in a list +template +constexpr static std::size_t length() { return apply::value; } + +/// Concatenate two or more typelists together +template +using concat = typename impl::concat::type; + +/// Create a list of sequential integers ranging from [0, count) +template +using make_sequence = typename impl::make_sequence::type; + +/// Template to build typelists using the following syntax: +/// builder<>::type::add::add::add[...]::finalize +/// Or: +/// builder<>::type::add_list>::add_list>[...]::finalize +template> +struct builder { + template using add = typename builder>::type>::type; + template using add_list = typename builder::type>::type; + using type = builder; + using finalize = List; +}; + +/// Transform elements of a typelist +template +using transform = typename impl::transform::type; + +/// Get the index of the given type within a list, or -1 if type is not found +template +constexpr static int index_of() { return impl::index_of::value; } + +/// Check if a given type is in a list +template +constexpr static bool contains() { return impl::index_of::value != -1; } + +/// Remove duplicate items from one or more typelists and concatenate them all together +template +using concat_unique = typename impl::concat_unique, TypeLists...>::type; + +/// Get the type at the specified list index +template +using at = typename impl::at::type; + +/// Get the type at the beginning of the list +template +using first = at; +/// Get the type at the end of the list +template +using last = at()-1>; + +/// Get the list with the element at the given index removed +template +using remove_at = typename impl::remove_at, List, index>::type; + +/// Get the list with the given type removed +template +using remove_element = remove_at()>; + +/// Get a list with all elements that do not pass a filter removed +template class Filter> +using filter = typename impl::filter, List>::type; + +/// Template to invert a filter, i.e. filter::type> +template class Filter> +struct invert_filter { + template + struct type { constexpr static bool value = !Filter::value; }; +}; + +/// Take the sublist at indexes [start, end) +template()> +using slice = typename impl::slice, List, start, end>::type; + +/// Zip two equal-length typelists together, i.e. zip, list> == list, list> +template +using zip = typename impl::zip::type; + +/// Add indexes to types in the list, i.e. index> == list, list<1, B>, list<2, C>> where +/// 0, 1, and 2 are std::integral_constants of type std::size_t +template +using index = typename impl::zip()>::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 struct wrapper { using type = T; }; + +/** + * @brief Index into the typelist for a type T, and invoke the callable with an argument wrapper() + * @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::value != 0>, + typename Return = decltype(std::declval()(wrapper, 0>>()))> +Return dispatch(list, std::size_t index, Callable c) { + static std::function call_table[] = + { impl::dispatch_helper>... }; + if (index < impl::length::value) return call_table[index](c); + throw std::out_of_range("Invalid index to fc::typelist::runtime::dispatch()"); +} +template +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() for each type in the list +template +void for_each(list, Callable c) { + bool trues[] = { [](Callable& c, auto t) { c(t); return true; }(c, wrapper())... }; + (void)(trues); +} + +} } } // namespace fc::typelist::runtime diff --git a/include/fc/reflect/typename.hpp b/include/fc/reflect/typename.hpp index 41fc348..15b8523 100644 --- a/include/fc/reflect/typename.hpp +++ b/include/fc/reflect/typename.hpp @@ -112,6 +112,22 @@ namespace fc { return _name.c_str(); } }; + template struct get_typename< const T* > + { + static const char* name() + { + static std::string n = std::string("const ") + get_typename::name() + "*"; + return n.c_str(); + } + }; + template struct get_typename< T* > + { + static const char* name() + { + static std::string n = std::string(get_typename::name()) + "*"; + return n.c_str(); + } + }; struct unsigned_int; class variant_object; diff --git a/include/fc/static_variant.hpp b/include/fc/static_variant.hpp index 89baa4e..d284963 100644 --- a/include/fc/static_variant.hpp +++ b/include/fc/static_variant.hpp @@ -23,174 +23,6 @@ namespace fc { // Implementation details, the user should not import this: namespace impl { -template -struct storage_ops; - -template -struct position; - -template -struct type_info; - -template -struct copy_construct -{ - typedef void result_type; - StaticVariant& sv; - copy_construct( StaticVariant& s ):sv(s){} - template - void operator()( const T& v )const - { - sv.init(v); - } -}; - -template -struct move_construct -{ - typedef void result_type; - StaticVariant& sv; - move_construct( StaticVariant& s ):sv(s){} - template - void operator()( T& v )const - { - sv.init( std::move(v) ); - } -}; - -template -struct storage_ops { - static void del(int n, void *data) {} - static void con(int n, void *data) {} - - template - static typename visitor::result_type apply(int n, void *data, visitor& v) {} - - template - static typename visitor::result_type apply(int n, void *data, const visitor& v) {} - - template - static typename visitor::result_type apply(int n, const void *data, visitor& v) {} - - template - static typename visitor::result_type apply(int n, const void *data, const visitor& v) {} -}; - -template -struct storage_ops { - static void del(int n, void *data) { - if(n == N) reinterpret_cast(data)->~T(); - else storage_ops::del(n, data); - } - static void con(int n, void *data) { - if(n == N) new(reinterpret_cast(data)) T(); - else storage_ops::con(n, data); - } - - template - static typename visitor::result_type apply(int n, void *data, visitor& v) { - if(n == N) return v(*reinterpret_cast(data)); - else return storage_ops::apply(n, data, v); - } - - template - static typename visitor::result_type apply(int n, void *data, const visitor& v) { - if(n == N) return v(*reinterpret_cast(data)); - else return storage_ops::apply(n, data, v); - } - - template - static typename visitor::result_type apply(int n, const void *data, visitor& v) { - if(n == N) return v(*reinterpret_cast(data)); - else return storage_ops::apply(n, data, v); - } - - template - static typename visitor::result_type apply(int n, const void *data, const visitor& v) { - if(n == N) return v(*reinterpret_cast(data)); - else return storage_ops::apply(n, data, v); - } -}; - -template -struct storage_ops { - 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 - 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 - 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 - 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 - 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 -struct position { - static constexpr int pos = -1; -}; - -template -struct position { - static constexpr int pos = 0; -}; - -template -struct position { - static constexpr int pos = position::pos != -1 ? position::pos + 1 : -1; -}; - -template -struct type_info { - static constexpr bool no_reference_types = false; - static constexpr bool no_duplicates = position::pos == -1 && type_info::no_duplicates; - static constexpr size_t size = type_info::size > sizeof(T&) ? type_info::size : sizeof(T&); - static constexpr size_t count = 1 + type_info::count; -}; - -template -struct type_info { - static constexpr bool no_reference_types = type_info::no_reference_types; - static constexpr bool no_duplicates = position::pos == -1 && type_info::no_duplicates; - static constexpr size_t size = type_info::size > sizeof(T) ? type_info::size : sizeof(T&); - static constexpr size_t count = 1 + type_info::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 -constexpr size_t size( TTag ) -{ - return 0; -} - -template -constexpr size_t size( TTag tag ) -{ - return tag <= 0 ? sizeof(A) : size( --tag ); -} - - class dynamic_storage { char* storage; @@ -206,71 +38,36 @@ public: void release(); }; - - } // namespace impl -template -static const std::array - init_wrappers( Visitor& v, Data d, typename Visitor::result_type(**funcs)(Visitor&,Data) = 0) -{ - return std::array(); -} - -template -static const std::array - init_wrappers( Visitor& v, Data d, typename Visitor::result_type(**funcs)(Visitor&,Data) = 0 ) -{ - std::array result{}; - if( !funcs ) funcs = result.data(); - *funcs++ = [] ( Visitor& v, Data d ) { return v( *reinterpret_cast( d ) ); }; - init_wrappers( v, d, funcs ); - return result; -} - -template -static const std::array - init_const_wrappers( Visitor& v, Data d, typename Visitor::result_type(**funcs)(Visitor&,Data) = 0 ) -{ - return std::array(); -} - -template -static const std::array - init_const_wrappers( Visitor& v, Data d, typename Visitor::result_type(**funcs)(Visitor&,Data) = 0 ) -{ - std::array result{}; - if( !funcs ) funcs = result.data(); - *funcs++ = [] ( Visitor& v, Data d ) { return v( *reinterpret_cast( d ) ); }; - init_const_wrappers( v, d, funcs ); - return result; -} - template class static_variant { public: using tag_type = int64_t; + using list = typelist::list; protected: - static_assert(impl::type_info::no_reference_types, "Reference types are not permitted in static_variant."); - static_assert(impl::type_info::no_duplicates, "static_variant type arguments contain duplicate types."); + static_assert(typelist::length>() == 0, + "Reference types are not permitted in static_variant."); + static_assert(typelist::length>() == typelist::length(), + "static_variant type arguments contain duplicate types."); template - using type_in_typelist = typename std::enable_if::pos != -1, X>::type; // type is in typelist of static_variant. + using type_in_typelist = std::enable_if_t() != -1>; tag_type _tag; impl::dynamic_storage storage; template> void init(const X& x) { - _tag = impl::position::pos; + _tag = typelist::index_of(); storage.alloc( sizeof(X) ); new(storage.data()) X(x); } template> void init(X&& x) { - _tag = impl::position::pos; + _tag = typelist::index_of(); 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 ) ); - 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(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(data)->~T(); + }); storage.release(); } + template + 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::name())); + } + static static_variant construct(T&&) { + FC_THROW_EXCEPTION(assert_exception, "Cannot import unsupported type ${T} into static_variant", + ("T", get_typename::name())); + } + }; + template + struct import_helper> { + static static_variant construct(const T& t) { + return static_variant(t); + } + static static_variant construct(T&& t) { + return static_variant(std::move(t)); + } + }; - template - friend struct impl::copy_construct; - template - friend struct impl::move_construct; public: template> struct tag { - static constexpr int value = impl::position::pos; + static const int value = typelist::index_of(); }; struct type_lt { @@ -310,6 +129,25 @@ public: }; using flat_set_type = flat_set; + /// Import the value from a foreign static_variant with types not in this one, and throw if the value is an + /// incompatible type + template + static static_variant import_from(const static_variant& other) { + return typelist::runtime::dispatch(typelist::list(), other.which(), [&other](auto t) { + using other_type = typename decltype(t)::type; + return import_helper::construct(other.template get()); + }); + } + /// Import the value from a foreign static_variant with types not in this one, and throw if the value is an + /// incompatible type + template + static static_variant import_from(static_variant&& other) { + return typelist::runtime::dispatch(typelist::list(), other.which(), [&other](auto t) { + using other_type = typename decltype(t)::type; + return import_helper::construct(std::move(other.template get())); + }); + } + static_variant() { init_from_tag(0); @@ -318,23 +156,41 @@ public: template static_variant( const static_variant& cpy ) { - cpy.visit( impl::copy_construct(*this) ); + typelist::runtime::dispatch(typelist::list(), cpy.which(), [this, &cpy](auto t) mutable { + this->init(cpy.template get()); + }); } static_variant( const static_variant& cpy ) { - cpy.visit( impl::copy_construct(*this) ); + typelist::runtime::dispatch(list(), cpy.which(), [this, &cpy](auto t) mutable { + this->init(cpy.template get()); + }); } static_variant( static_variant&& mv ) { - mv.visit( impl::move_construct(*this) ); + typelist::runtime::dispatch(list(), mv.which(), [this, &mv](auto t) mutable { + this->init(std::move(mv.template get())); + }); + } + + template + static_variant( static_variant&& mv ) + { + typelist::runtime::dispatch(typelist::list(), mv.which(), [this, &mv](auto t) mutable { + this->init(std::move(mv.template get())); + }); } template> static_variant(const X& v) { init(v); } + template> + static_variant(X&& v) { + init(std::move(v)); + } ~static_variant() { clean(); @@ -350,20 +206,24 @@ public: { if( this == &v ) return *this; clean(); - v.visit( impl::copy_construct(*this) ); + typelist::runtime::dispatch(list(), v.which(), [this, &v](auto t)mutable { + this->init(v.template get()); + }); return *this; } static_variant& operator=( static_variant&& v ) { if( this == &v ) return *this; clean(); - v.visit( impl::move_construct(*this) ); + typelist::runtime::dispatch(list(), v.which(), [this, &v](auto t)mutable { + this->init(std::move(v.template get())); + }); return *this; } template> X& get() { - if(_tag == impl::position::pos) { + if(_tag == typelist::index_of()) { return *reinterpret_cast(storage.data()); } else { FC_THROW_EXCEPTION( fc::assert_exception, "static_variant does not contain a value of type ${t}", ("t",fc::get_typename::name()) ); @@ -371,7 +231,7 @@ public: } template> const X& get() const { - if(_tag == impl::position::pos) { + if(_tag == typelist::index_of()) { return *reinterpret_cast(storage.data()); } else { FC_THROW_EXCEPTION( fc::assert_exception, "static_variant does not contain a value of type ${t}", ("t",fc::get_typename::name()) ); @@ -400,36 +260,40 @@ public: template static typename visitor::result_type visit( tag_type tag, visitor& v, void* data ) { - static const auto wrappers = init_wrappers::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(data)); + }); } template static typename visitor::result_type visit( tag_type tag, const visitor& v, void* data ) { - static const auto wrappers = init_wrappers::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(data)); + }); } template static typename visitor::result_type visit( tag_type tag, visitor& v, const void* data ) { - static const auto wrappers = init_const_wrappers::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(data)); + }); } template static typename visitor::result_type visit( tag_type tag, const visitor& v, const void* data ) { - static const auto wrappers = init_const_wrappers::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(data)); + }); } - static constexpr int count() { return impl::type_info::count; } + static int count() { return typelist::length(); } void set_which( tag_type w ) { FC_ASSERT( w >= 0 ); FC_ASSERT( w < count() ); @@ -442,6 +306,12 @@ public: template bool is_type() const { return _tag == tag::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 class static_variant> : public static_variant {}; struct from_static_variant { diff --git a/src/network/http/websocket.cpp b/src/network/http/websocket.cpp index 5138fb0..45332f5 100644 --- a/src/network/http/websocket.cpp +++ b/src/network/http/websocket.cpp @@ -300,6 +300,10 @@ namespace fc { namespace http { _server.close( item.first, 0, "server exit" ); if( _closed ) _closed->wait(); +#ifdef TRAVIS_BUILD +#warning Adding 100ms sleep to ~websocket_server_impl() to stabilize Travis tests + fc::usleep(fc::milliseconds(100)); +#endif } typedef std::map > con_map; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 01cf205..5c17ca7 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -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 diff --git a/tests/reflection_tests.cpp b/tests/reflection_tests.cpp new file mode 100644 index 0000000..c4e3491 --- /dev/null +++ b/tests/reflection_tests.cpp @@ -0,0 +1,126 @@ +#include + +#include + +#include + +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; + using derived_reflection = fc::reflector; + static_assert(fc::typelist::length() == 2, ""); + static_assert(fc::typelist::length() == 3, ""); + static_assert(fc::typelist::at::is_derived, ""); + static_assert(std::is_same::field_container, + reflect_test_base>::value, ""); + static_assert(fc::typelist::at::is_derived, ""); + static_assert(std::is_same::field_container, + reflect_test_base>::value, ""); + static_assert(fc::typelist::at::is_derived == false, ""); + static_assert(std::is_same, 0, 1>, + fc::typelist::list>::value, ""); + static_assert(std::is_same, 0, 2>, + fc::typelist::list>::value, ""); + static_assert(std::is_same, 0, 3>, + fc::typelist::list>::value, ""); + static_assert(std::is_same, 1, 3>, + fc::typelist::list>::value, ""); + static_assert(std::is_same, 2, 3>, + fc::typelist::list>::value, ""); + static_assert(std::is_same, 1, 2>, + fc::typelist::list>::value, ""); + static_assert(std::is_same, 1>, + fc::typelist::list>::value, ""); + static_assert(std::is_same, fc::typelist::list<>>::value, ""); + static_assert(std::is_same, + fc::typelist::list>>::value, ""); + static_assert(std::is_same, + fc::typelist::list, + std::integral_constant>>::value, ""); + static_assert(std::is_same, + fc::typelist::list, + std::integral_constant, + std::integral_constant>>::value, ""); + static_assert(std::is_same, fc::typelist::list<>>, + fc::typelist::list<>>::value, ""); + static_assert(std::is_same, fc::typelist::list>, + fc::typelist::list>>::value, ""); + static_assert(std::is_same, fc::typelist::list>, + fc::typelist::list, + fc::typelist::list>>::value, ""); + static_assert(std::is_same>, fc::typelist::list<>>::value, ""); + static_assert(std::is_same>, + fc::typelist::list, int>, + fc::typelist::list, bool>, + fc::typelist::list, char>, + fc::typelist::list, double>> + >::value, ""); +} + +BOOST_AUTO_TEST_CASE( typelist_dispatch_test ) +{ + using list = fc::typelist::list; + auto get_name = [](auto t) -> std::string { return fc::get_typename::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 struct index_from { template using at = fc::typelist::at; }; +BOOST_AUTO_TEST_CASE( reflection_get_test ) +{ try { + reflect_test_derived derived; + reflect_test_base& base = derived; + + using base_reflector = fc::reflector; + using derived_reflector = fc::reflector; + + BOOST_CHECK(index_from::at<0>::get(base) == 1); + BOOST_CHECK(index_from::at<1>::get(base) == 'a'); + + fc::typelist::at::get(base) = 5; + fc::typelist::at::get(base) = 'q'; + + BOOST_CHECK(index_from::at<0>::get(base) == 5); + BOOST_CHECK(index_from::at<1>::get(base) == 'q'); + + BOOST_CHECK(index_from::at<0>::get(derived) == 5); + BOOST_CHECK(index_from::at<1>::get(derived) == 'q'); + BOOST_CHECK(index_from::at<2>::get(derived) == 3.14); + + fc::typelist::at::get(derived) = 'X'; + + BOOST_CHECK(index_from::at<1>::get(base) == 'X'); + + reflect_layer_3 l3; + BOOST_CHECK(index_from::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::members>::at<0>::reflector::members> + ::at<1>::reflector::members>::at<1>::get(l3.l2.d) == 'a'); + BOOST_CHECK(index_from::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() diff --git a/tests/serialization_test.cpp b/tests/serialization_test.cpp index 1e0165c..ccfe8d2 100644 --- a/tests/serialization_test.cpp +++ b/tests/serialization_test.cpp @@ -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) );