From 135795a5f368cc2855549064c232ba79520e9593 Mon Sep 17 00:00:00 2001 From: arhag Date: Thu, 21 Sep 2017 23:07:18 -0400 Subject: [PATCH] Modified ABI to include an explicit tuple/pair type (i.e. an anonymous struct) (for EOSIO/eos#354): * Modified ABI to allow tuple types which do not require specifying a name for the type nor names for the "fields". * Updated types_constructor to support the new tuple type. * Updated reflection macros to still automatically work with std::pair and std::tuple but now without depending on implementation-specific C++ type names. * Found and corrected a small bug in `abi_constructor::add_variant`. * Tidying up. --- libraries/types/abi_constructor.cpp | 34 ++- libraries/types/include/eos/types/abi.hpp | 2 +- .../include/eos/types/abi_constructor.hpp | 6 +- .../include/eos/types/abi_definition.hpp | 3 +- .../include/eos/types/deserialize_visitor.hpp | 2 +- libraries/types/include/eos/types/reflect.hpp | 170 ++++++------- .../eos/types/serialization_region.hpp | 2 +- .../include/eos/types/types_constructor.hpp | 5 +- libraries/types/types_constructor.cpp | 237 +++++++++++++----- programs/reflection_test1.cpp | 1 - 10 files changed, 289 insertions(+), 173 deletions(-) diff --git a/libraries/types/abi_constructor.cpp b/libraries/types/abi_constructor.cpp index 0889822..e7c45da 100644 --- a/libraries/types/abi_constructor.cpp +++ b/libraries/types/abi_constructor.cpp @@ -58,6 +58,28 @@ namespace eos { namespace types { return static_cast(itr->second); } + type_id::index_t abi_constructor::add_tuple(const vector& fields) + { + if( fields.size() < 2 ) + throw std::invalid_argument("There must be at least 2 fields within the tuple."); + + auto itr = tuple_lookup.find(fields); + if( itr != tuple_lookup.end() ) + return itr->second; + + if( abi.type_sequences.size() >= type_id::type_index_limit ) + throw std::logic_error("Too many variants or tuples defined."); + + type_id::index_t index = abi.types.size(); + abi.types.push_back({ .first = static_cast(abi.type_sequences.size()), .second = static_cast(fields.size()), .ts = ABI::tuple_type }); + for( auto t : fields ) + abi.type_sequences.push_back(t); + + tuple_lookup.emplace(fields, index); + return index; + + } + type_id::index_t abi_constructor::add_array(type_id element_type, uint32_t num_elements) { if( num_elements < 2 ) @@ -74,7 +96,6 @@ namespace eos { namespace types { abi.types.push_back({ .first = element_type.get_storage(), .second = static_cast(num_elements), .ts = ABI::array_type }); array_lookup.emplace(std::make_pair(element_type, num_elements), index); - valid_array_start_indices.insert(index); return index; } @@ -88,7 +109,6 @@ namespace eos { namespace types { abi.types.push_back({ .first = element_type.get_storage(), .second = -1, .ts = ABI::vector_type }); vector_lookup.emplace(element_type, index); - valid_vector_start_indices.insert(index); return index; } @@ -102,7 +122,6 @@ namespace eos { namespace types { abi.types.push_back({ .first = element_type.get_storage(), .second = -1, .ts = ABI::optional_type }); optional_lookup.emplace(element_type, index); - valid_sum_type_start_indices.insert(index); return index; } @@ -118,16 +137,15 @@ namespace eos { namespace types { if( itr != variant_lookup.end() ) return itr->second; - if( abi.variant_cases.size() >= type_id::type_index_limit ) - throw std::logic_error("Too many variants defined."); + if( abi.type_sequences.size() >= type_id::type_index_limit ) + throw std::logic_error("Too many variants or tuples defined."); type_id::index_t index = abi.types.size(); - abi.types.push_back({ .first = static_cast(abi.variant_cases.size()), .second = static_cast(cases.size()), .ts = ABI::optional_type }); + abi.types.push_back({ .first = static_cast(abi.type_sequences.size()), .second = static_cast(cases.size()), .ts = ABI::variant_type }); for( auto t : cases ) - abi.variant_cases.push_back(t); + abi.type_sequences.push_back(t); variant_lookup.emplace(cases, index); - valid_sum_type_start_indices.insert(index); return index; } diff --git a/libraries/types/include/eos/types/abi.hpp b/libraries/types/include/eos/types/abi.hpp index ca9e33a..d76189a 100644 --- a/libraries/types/include/eos/types/abi.hpp +++ b/libraries/types/include/eos/types/abi.hpp @@ -8,4 +8,4 @@ EOS_TYPES_REFLECT_STRUCT( eos::types::ABI::type_definition, (first)(second)(ts) EOS_TYPES_REFLECT_STRUCT( eos::types::ABI::struct_t, (name)(fields)(sort_order) ) EOS_TYPES_REFLECT_STRUCT( eos::types::ABI::table_index, (key_type)(unique)(ascending)(mapping) ) EOS_TYPES_REFLECT_STRUCT( eos::types::ABI::table, (object_index)(indices) ) -EOS_TYPES_REFLECT_STRUCT( eos::types::ABI, (types)(structs)(variant_cases)(tables) ) +EOS_TYPES_REFLECT_STRUCT( eos::types::ABI, (types)(structs)(type_sequences)(tables) ) diff --git a/libraries/types/include/eos/types/abi_constructor.hpp b/libraries/types/include/eos/types/abi_constructor.hpp index 3814756..549e6f6 100644 --- a/libraries/types/include/eos/types/abi_constructor.hpp +++ b/libraries/types/include/eos/types/abi_constructor.hpp @@ -18,6 +18,7 @@ namespace eos { namespace types { type_id::index_t declare_struct(const char* name); type_id::index_t add_struct(const char* name, const vector>& fields, const vector& sort_order, const char* base_name = nullptr); + type_id::index_t add_tuple(const vector& fields); type_id::index_t add_array(type_id element_type, uint32_t num_elements); type_id::index_t add_vector(type_id element_type); type_id::index_t add_optional(type_id element_type); @@ -37,10 +38,7 @@ namespace eos { namespace types { map vector_lookup; map optional_lookup; map, type_id::index_t> variant_lookup; - - set valid_array_start_indices; - set valid_vector_start_indices; - set valid_sum_type_start_indices; + map, type_id::index_t> tuple_lookup; }; diff --git a/libraries/types/include/eos/types/abi_definition.hpp b/libraries/types/include/eos/types/abi_definition.hpp index 1fc4edc..e7b68fc 100644 --- a/libraries/types/include/eos/types/abi_definition.hpp +++ b/libraries/types/include/eos/types/abi_definition.hpp @@ -17,6 +17,7 @@ namespace eos { namespace types { enum type_specification : uint8_t { struct_type, + tuple_type, array_type, vector_type, optional_type, @@ -54,7 +55,7 @@ namespace eos { namespace types { vector types; vector structs; - vector variant_cases; + vector type_sequences; vector tables; }; diff --git a/libraries/types/include/eos/types/deserialize_visitor.hpp b/libraries/types/include/eos/types/deserialize_visitor.hpp index a61950b..b59000d 100644 --- a/libraries/types/include/eos/types/deserialize_visitor.hpp +++ b/libraries/types/include/eos/types/deserialize_visitor.hpp @@ -125,7 +125,7 @@ EOS_TYPES_CUSTOM_BUILTIN_MATCH_END } template // Meant for tuples/pairs - typename std::enable_if::is_struct::value>::type + typename std::enable_if::is_tuple::value>::type operator()(Class& c)const { auto vis = make_visitor_for_product_type_member(static_cast(Index)); diff --git a/libraries/types/include/eos/types/reflect.hpp b/libraries/types/include/eos/types/reflect.hpp index 437af95..fd4a99d 100644 --- a/libraries/types/include/eos/types/reflect.hpp +++ b/libraries/types/include/eos/types/reflect.hpp @@ -126,28 +126,10 @@ namespace eos { namespace types { template <> \ struct reflector \ { \ - using is_defined = std::true_type; \ - using is_struct = std::true_type; \ - using type = T; - -#define EOS_TYPES_REFLECT_STRUCT_END_HELPER(NAME) \ - static inline uint32_t register_struct(eos::types::abi_constructor& ac, const std::vector& ft)\ - { \ - const char* * field_names = nullptr; \ - const char* * sorted_member_names = nullptr; \ - bool * sorted_member_dir = nullptr; \ - get_member_info(&field_names, &sorted_member_names, &sorted_member_dir); \ - return eos::types::register_struct(ac, ft, NAME, \ - field_names, static_cast(field_count), \ - sorted_member_names, sorted_member_dir, \ - static_cast(sorted_member_count)); \ - } \ - static inline const char* name() \ - { \ - return NAME; \ - } \ - }; \ -} } + using is_defined = std::true_type; \ + using is_product_type = std::true_type; \ + using is_struct = std::true_type; \ + using type = T; #define EOS_TYPES_REFLECT_STRUCT_END(T, fields) \ template \ @@ -167,7 +149,23 @@ namespace eos { namespace types { { \ BOOST_PP_SEQ_FOR_EACH_I(EOS_TYPES_REFLECT_VISIT_MEMBER, 0, fields) \ } \ - EOS_TYPES_REFLECT_STRUCT_END_HELPER(BOOST_PP_STRINGIZE(T)) + static inline uint32_t register_struct(eos::types::abi_constructor& ac, const std::vector& ft)\ + { \ + const char* * field_names = nullptr; \ + const char* * sorted_member_names = nullptr; \ + bool * sorted_member_dir = nullptr; \ + get_member_info(&field_names, &sorted_member_names, &sorted_member_dir); \ + return eos::types::register_struct(ac, ft, BOOST_PP_STRINGIZE(T), \ + field_names, static_cast(field_count), \ + sorted_member_names, sorted_member_dir, \ + static_cast(sorted_member_count)); \ + } \ + static inline const char* name() \ + { \ + return BOOST_PP_STRINGIZE(T); \ + } \ + }; \ +} } #define EOS_TYPES_REFLECT_STRUCT_1 @@ -196,9 +194,10 @@ namespace eos { namespace types { template <> \ struct reflector \ { \ - using is_defined = std::true_type; \ - using is_struct = std::true_type; \ - using type = T; + using is_defined = std::true_type; \ + using is_product_type = std::true_type; \ + using is_struct = std::true_type; \ + using type = T; #define EOS_TYPES_REFLECT_STRUCT_DERIVED_END(T, B, fields) \ template \ @@ -268,10 +267,6 @@ EOS_TYPES_REFLECT_STRUCT_DERIVED_END(T, B, fields) BOOST_PP_CAT(BOOST_PP_OVERLOAD(EOS_TYPES_REFLECT_STRUCT_DERIVED_,__VA_ARGS__)(__VA_ARGS__),BOOST_PP_EMPTY()) #endif -//#define EOS_TYPES_REFLECT_GET_TYPENAME(T) BOOST_PP_STRINGIZE(T) - -#define EOS_TYPES_REFLECT_GET_TYPENAME(T) typeid(T).name() - #define EOS_TYPES_REFLECT_TEMPLATE_ARG(r, index, data) , data T##index #define EOS_TYPES_REFLECT_VISIT_TUPLE_ELEMENT( r, index, data ) \ @@ -280,23 +275,28 @@ EOS_TYPES_REFLECT_STRUCT_DERIVED_END(T, B, fields) #define EOS_TYPES_REFLECT_VISIT_TUPLE_ELEMENT_TYPE( r, index, data ) \ _v.TEMPLATE operator()(); -#define EOS_TYPES_REFLECT_PRODUCT_TYPE_HELPER(C, num_template_args, NAME, fields, member_sort) \ +#define EOS_TYPES_REFLECT_TUPLE(C, num_template_args) \ namespace eos { namespace types { \ template \ struct reflector> \ { \ - using is_defined = std::true_type; \ - using is_struct = std::true_type; \ - using type = C; \ -EOS_TYPES_REFLECT_MEMBER_COUNT(fields, member_sort) \ -EOS_TYPES_REFLECT_GET_MEMBER_INFO(fields, member_sort) \ + using is_defined = std::true_type; \ + using is_product_type = std::true_type; \ + using is_struct = std::false_type; \ + using is_tuple = std::true_type; \ + using type = C; \ + enum field_count_enum { \ + field_count = num_template_args \ + }; \ + enum sorted_member_count_enum { \ + sorted_member_count = num_template_args \ + }; \ template \ static void visit(Visitor& _v) \ { \ - if( !_v.TEMPLATE operator()( NAME ) ) { return; } \ BOOST_PP_REPEAT(num_template_args, EOS_TYPES_REFLECT_VISIT_TUPLE_ELEMENT_TYPE, T) \ _v.TEMPLATE operator()(); \ } \ @@ -310,32 +310,8 @@ EOS_TYPES_REFLECT_GET_MEMBER_INFO(fields, member_sort) { \ BOOST_PP_REPEAT(num_template_args, EOS_TYPES_REFLECT_VISIT_TUPLE_ELEMENT, T) \ } \ -EOS_TYPES_REFLECT_STRUCT_END_HELPER(NAME) - -#define EOS_TYPES_REFLECT_ANONYMOUS_FIELD(r, index, data) (data##index) - -#define EOS_TYPES_REFLECT_ANONYMOUS_FIELD_ASCENDING(r, index, data) ((data##index, asc)) - -#if 0 - -#define EOS_TYPES_STRINGIZE_ALL_HELPER(...) #__VA_ARGS__ - -#define EOS_TYPES_STRINGIZE_ALL(...) EOS_TYPES_STRINGIZE_ALL_HELPER(__VA_ARGS__) - -#define EOS_TYPES_REFLECT_PRODUCT_TYPE(C, Ts) \ -EOS_TYPES_REFLECT_PRODUCT_TYPE_HELPER(C, BOOST_PP_SEQ_SIZE(Ts), \ - EOS_TYPES_STRINGIZE_ALL(C), \ - BOOST_PP_REPEAT(BOOST_PP_SEQ_SIZE(Ts), EOS_TYPES_REFLECT_ANONYMOUS_FIELD, f), \ - BOOST_PP_REPEAT(BOOST_PP_SEQ_SIZE(Ts), EOS_TYPES_REFLECT_ANONYMOUS_FIELD_ASCENDING, f) ) - -#endif - -#define EOS_TYPES_REFLECT_TUPLE(C, num_template_args) \ -EOS_TYPES_REFLECT_PRODUCT_TYPE_HELPER(C, num_template_args, \ - EOS_TYPES_REFLECT_GET_TYPENAME(type), \ - BOOST_PP_REPEAT(num_template_args, EOS_TYPES_REFLECT_ANONYMOUS_FIELD, f), \ - BOOST_PP_REPEAT(num_template_args, EOS_TYPES_REFLECT_ANONYMOUS_FIELD_ASCENDING, f) ) - + }; \ +} } #define EOS_TYPES_REFLECT_SIMPLE_VISIT \ template \ @@ -359,10 +335,10 @@ namespace eos { namespace types { \ template <> \ struct reflector \ { \ - using is_defined = std::true_type; \ - using is_struct = std::false_type; \ - using is_builtin = std::true_type; \ - using type = T; \ + using is_defined = std::true_type; \ + using is_struct = std::false_type; \ + using is_builtin = std::true_type; \ + using type = T; \ static const type_id::builtin builtin_type = type_id::B; \ EOS_TYPES_REFLECT_SIMPLE_VISIT \ }; \ @@ -373,10 +349,10 @@ namespace eos { namespace types { \ template \ struct reflector> \ { \ - using is_defined = std::true_type; \ - using is_struct = std::false_type; \ - using is_array = std::true_type; \ - using type = C; \ + using is_defined = std::true_type; \ + using is_struct = std::false_type; \ + using is_array = std::true_type; \ + using type = C; \ enum num_elements_enum { \ num_elements = N \ }; \ @@ -389,10 +365,10 @@ namespace eos { namespace types { \ template \ struct reflector> \ { \ - using is_defined = std::true_type; \ - using is_struct = std::false_type; \ - using is_vector = std::true_type; \ - using type = C; \ + using is_defined = std::true_type; \ + using is_struct = std::false_type; \ + using is_vector = std::true_type; \ + using type = C; \ EOS_TYPES_REFLECT_SIMPLE_VISIT \ }; \ } } @@ -402,10 +378,10 @@ namespace eos { namespace types { \ template \ struct reflector> \ { \ - using is_defined = std::true_type; \ - using is_struct = std::false_type; \ - using is_optional = std::true_type; \ - using type = C; \ + using is_defined = std::true_type; \ + using is_struct = std::false_type; \ + using is_optional = std::true_type; \ + using type = C; \ EOS_TYPES_REFLECT_SIMPLE_VISIT \ }; \ } } @@ -578,20 +554,10 @@ namespace eos { namespace types { } template - typename std::enable_if::is_struct::value>::type - operator()(bool unique, bool ascending, const vector& mapping) // Table index of struct key type + typename std::enable_if::is_product_type::value>::type + operator()(bool unique, bool ascending, const vector& mapping) // Table index of struct or tuple/pair key type { - auto cur_struct_index = ac.get_struct_index(eos::types::reflector::name()); - if( cur_struct_index >= 0 ) - { - tid = type_id::make_struct(static_cast(cur_struct_index)); - } - else - { - type_discovery_visitor vis(ac); - eos::types::reflector::visit(vis); - tid = vis.tid; - } + eos::types::reflector::visit(*this); // Should modify tid to be the type_id of Class indices.push_back(ABI::table_index{ .key_type = tid, .unique = unique, .ascending = ascending, .mapping = mapping }); } @@ -611,7 +577,7 @@ namespace eos { namespace types { template typename std::enable_if::is_struct::value, bool>::type - operator()(const char* name) // Called before processing the fields of a product type such as a struct or its base or a tuple/pair. + operator()(const char* name) // Called before processing the fields of a struct or its base. { auto cur_struct_index = ac.get_struct_index(eos::types::reflector::name()); if( cur_struct_index >= 0 ) @@ -625,7 +591,7 @@ namespace eos { namespace types { template typename std::enable_if::is_struct::value && eos::types::reflector::is_struct::value>::type - operator()()const + operator()()const // Called when processing the base of a struct. { if( ac.get_struct_index(eos::types::reflector::name()) >= 0 ) return; @@ -636,12 +602,20 @@ namespace eos { namespace types { template typename std::enable_if::is_struct::value>::type - operator()() // Called after processing the fields of the product type + operator()() // Called after processing the fields of a struct { tid = type_id::make_struct(eos::types::reflector::register_struct(ac, field_types)); field_types.clear(); } + template + typename std::enable_if::is_tuple::value>::type + operator()() // Called after processing the fields of a tuple/pair + { + tid = type_id::make_struct(ac.add_tuple(field_types)); + field_types.clear(); + } + template typename std::enable_if::is_struct::value && eos::types::reflector::is_struct::value>::type operator()(const char* name, uint32_t member_index) @@ -669,7 +643,7 @@ namespace eos { namespace types { } template // Meant for tuples/pairs - typename std::enable_if::is_struct::value && eos::types::reflector::is_struct::value>::type + typename std::enable_if::is_tuple::value && eos::types::reflector::is_struct::value>::type operator()() { auto index = ac.get_struct_index(eos::types::reflector::name()); @@ -686,7 +660,7 @@ namespace eos { namespace types { } template // Meant for tuples/pairs - typename std::enable_if::is_struct::value && !eos::types::reflector::is_struct::value>::type + typename std::enable_if::is_tuple::value && !eos::types::reflector::is_struct::value>::type operator()() { type_discovery_visitor vis(ac); diff --git a/libraries/types/include/eos/types/serialization_region.hpp b/libraries/types/include/eos/types/serialization_region.hpp index 981818b..3e6f4a3 100644 --- a/libraries/types/include/eos/types/serialization_region.hpp +++ b/libraries/types/include/eos/types/serialization_region.hpp @@ -138,7 +138,7 @@ EOS_TYPES_CUSTOM_BUILTIN_MATCH_END } template // Meant for tuples/pairs - typename std::enable_if::is_struct::value>::type + typename std::enable_if::is_tuple::value>::type operator()(const Class& c)const { auto vis = make_visitor_for_product_type_member(static_cast(Index)); diff --git a/libraries/types/include/eos/types/types_constructor.hpp b/libraries/types/include/eos/types/types_constructor.hpp index 9750691..584a592 100644 --- a/libraries/types/include/eos/types/types_constructor.hpp +++ b/libraries/types/include/eos/types/types_constructor.hpp @@ -35,6 +35,7 @@ namespace eos { namespace types { inline type_id::index_t forward_declare(const char* declared_name) { return forward_declare(string(declared_name)); } type_id::index_t forward_declare(const string& declared_name); type_id::index_t add_struct(const string& name, const vector>& fields, type_id base = type_id(), int16_t base_sort = 0 ); + type_id::index_t add_tuple(const vector& fields); type_id::index_t add_array(type_id element_type, uint32_t num_elements); type_id::index_t add_vector(type_id element_type); type_id::index_t add_optional(type_id element_type); @@ -52,7 +53,6 @@ namespace eos { namespace types { return get_struct_index(eos::types::reflector::name()); } - bool is_type_valid(type_id tid)const; inline bool is_disabled()const { return disabled; } bool is_complete()const; @@ -85,6 +85,7 @@ namespace eos { namespace types { type_id::index_t active_struct_index = type_id::type_index_limit; map struct_lookup; + map, type_id::index_t> tuple_lookup; map, type_id::index_t> array_lookup; map vector_lookup; map optional_lookup; @@ -92,6 +93,7 @@ namespace eos { namespace types { map table_lookup; map valid_struct_start_indices; + set valid_tuple_start_indices; set valid_array_start_indices; set valid_vector_start_indices; set valid_sum_type_start_indices; @@ -105,6 +107,7 @@ namespace eos { namespace types { vector struct_views; void check_disabled()const; + void check_struct_index(type_id::index_t struct_index)const; void name_conflict_check(const string& name, bool skip_structs = false)const; pair process_struct(const vector>& fields, type_id base, int16_t base_sort); diff --git a/libraries/types/types_constructor.cpp b/libraries/types/types_constructor.cpp index d4c140c..119e144 100644 --- a/libraries/types/types_constructor.cpp +++ b/libraries/types/types_constructor.cpp @@ -137,6 +137,26 @@ namespace eos { namespace types { return add_struct(s.name, fields, base, base_sort); } + case ABI::tuple_type: + { + uint32_t num_fields = static_cast(t.second); + if( (t.first + num_fields) > abi.type_sequences.size() ) + throw std::out_of_range("Invalid tuple specification."); + + vector fields; + fields.reserve(num_fields); + auto itr = abi.type_sequences.begin() + t.first; + for( uint32_t j = 0; j < num_fields; ++j, ++itr ) + { + auto t = remap_abi_type(abi, index_map, struct_map, *itr); + fields.push_back(t); + } + + auto index = add_tuple(fields); + struct_map.emplace(i, set()); + index_map.emplace(i, index); + return index; + } case ABI::array_type: { auto index = add_array(remap_abi_type(abi, index_map, struct_map, type_id(t.first)), t.second); @@ -160,12 +180,12 @@ namespace eos { namespace types { uint32_t num_cases = static_cast(t.second); if( num_cases >= type_id::variant_case_limit ) throw std::invalid_argument("Variant has too many cases."); - if( (t.first + num_cases) > abi.variant_cases.size() ) + if( (t.first + num_cases) > abi.type_sequences.size() ) throw std::out_of_range("Invalid variant specification."); vector cases; cases.reserve(num_cases); - auto itr = abi.variant_cases.begin() + t.first; + auto itr = abi.type_sequences.begin() + t.first; for( uint32_t j = 0; j < num_cases; ++j, ++itr ) { auto t = remap_abi_type(abi, index_map, struct_map, *itr); @@ -258,11 +278,11 @@ namespace eos { namespace types { continue; if( tc != type_id::struct_type ) - throw std::invalid_argument("Table index keys can only be either builtin types or structs."); + throw std::invalid_argument("Table index keys can only be either builtin types or product types (structs or tuples)."); auto key_index = indx.key_type.get_type_index(); - if( struct_map.find(key_index) == struct_map.end() ) - throw std::invalid_argument("Table index key refers either to an invalid index or to an index of a non-struct type."); + if( struct_map.find(key_index) == struct_map.end() ) + throw std::invalid_argument("Table index key refers either to an invalid index or to an index of type that is neither a struct or a tuple."); indices.back().key_type = remap_abi_type(abi, index_map, struct_map, type_id::make_struct(key_index)); } @@ -638,9 +658,74 @@ namespace eos { namespace types { return {type_id::size_align(size, align), fobso_map.size()}; } + type_id::index_t types_constructor::add_tuple(const vector& field_types) + { + check_disabled(); + + if( field_types.size() < 2 ) + throw std::invalid_argument("There must be at least 2 fields within the tuple."); + + if( field_types.size() >= type_id::struct_fields_limit ) + throw std::invalid_argument("There are too many fields within the tuple."); + + auto itr = tuple_lookup.find(field_types); + if( itr != tuple_lookup.end() ) + return itr->second; + + if( tm.types.size() >= type_id::type_index_limit) + throw std::logic_error("Not enough room to add a tuple with valid index."); + + if( (tm.members.size() + 2 * field_types.size()) > std::numeric_limits::max() ) + throw std::logic_error("Fields vector would become too large."); + + vector> fields; + fields.reserve(field_types.size()); + int16_t counter = 1; + for( auto t : field_types ) + { + if( !is_type_valid(t) ) + throw std::invalid_argument("Invalid type within tuple that was to be added."); + fields.emplace_back(t, counter); + ++counter; + } + + type_id::index_t index = tm.add_empty_struct_to_end(); + + active_struct_index = index; + uint32_t original_members_size = tm.members.size(); + try + { + type_id::size_align sa; + uint16_t num_sorted_members = 0; + std::tie(sa, num_sorted_members) = process_struct(fields, type_id(), 0); // Actually adds field_metadata to end of members + // If anything (meaning the process_struct function) throws an exception up to this point from the start of the try block, + // then this function will be able to recover the state prior to being called. + + tm.complete_struct(index, sa, + original_members_size, fields.size(), num_sorted_members, + type_id(), field_metadata::unsorted); + } + catch( ... ) + { + active_struct_index = type_id::type_index_limit; + tm.types.resize(index); + tm.members.resize(original_members_size); + throw; + } + active_struct_index = type_id::type_index_limit; + + tuple_lookup.emplace(field_types, index); + valid_tuple_start_indices.insert(index); + ++num_types_with_cacheable_size_align; + ++num_types_with_cached_size_align; + return index; + } + + type_id::index_t types_constructor::add_array(type_id element_type, uint32_t num_elements) { check_disabled(); + if( num_elements < 2 ) throw std::domain_error("Number of elements must be at least 2."); @@ -904,7 +989,12 @@ namespace eos { namespace types { auto itr = valid_struct_start_indices.find(index); if( itr == valid_struct_start_indices.end() ) - throw std::invalid_argument("Not a valid struct index."); + { + if( valid_tuple_start_indices.find(index) == valid_tuple_start_indices.end() ) + throw std::invalid_argument("Not a valid struct index."); + else + throw std::invalid_argument("Struct index points to a tuple and so it does not have a name."); + } return itr->second; } @@ -954,7 +1044,9 @@ namespace eos { namespace types { case type_id::struct_type: if( index == active_struct_index ) return true; - return (valid_struct_start_indices.find(index) != valid_struct_start_indices.end()); + else if( valid_struct_start_indices.find(index) != valid_struct_start_indices.end() ) + return true; + return (valid_tuple_start_indices.find(index) != valid_tuple_start_indices.end()); case type_id::array_type: return (valid_array_start_indices.find(index) != valid_array_start_indices.end()); case type_id::vector_type: @@ -991,8 +1083,10 @@ namespace eos { namespace types { if( num_types_with_cacheable_size_align == num_types_with_cached_size_align ) return; - for( const auto& p : valid_struct_start_indices) // Caching size_align of structs must come first. + for( const auto& p : valid_struct_start_indices ) // Caching size_align of structs and tuples must come first. get_size_align(type_id::make_struct(p.first)); + for( auto index : valid_tuple_start_indices ) + get_size_align(type_id::make_struct(index)); for( auto index : valid_array_start_indices) get_size_align(type_id::make_array(index)); @@ -1045,13 +1139,18 @@ namespace eos { namespace types { return tm.get_size_align(tid, &num_types_with_cached_size_align); } + void types_constructor::check_struct_index(type_id::index_t struct_index)const + { + if( (valid_struct_start_indices.find(struct_index) == valid_struct_start_indices.end()) + && (valid_tuple_start_indices.find(struct_index) == valid_tuple_start_indices.end()) ) + throw std::invalid_argument("Struct does not exist."); + } + type_id::size_align types_constructor::get_size_align_of_struct(type_id::index_t struct_index)const { check_disabled(); - - if( valid_struct_start_indices.find(struct_index) == valid_struct_start_indices.end() ) - throw std::invalid_argument("Struct does not exist."); + check_struct_index(struct_index); return get_size_align(type_id::make_struct(struct_index)); } @@ -1060,10 +1159,7 @@ namespace eos { namespace types { types_constructor::get_all_members(type_id::index_t struct_index)const { check_disabled(); - - auto itr = valid_struct_start_indices.find(struct_index); - if( itr == valid_struct_start_indices.end() ) - throw std::invalid_argument("Struct does not exist."); + check_struct_index(struct_index); return tm.get_all_members(struct_index); } @@ -1072,10 +1168,7 @@ namespace eos { namespace types { types_constructor::get_sorted_members(type_id::index_t struct_index)const { check_disabled(); - - auto itr = valid_struct_start_indices.find(struct_index); - if( itr == valid_struct_start_indices.end() ) - throw std::invalid_argument("Struct does not exist."); + check_struct_index(struct_index); return tm.get_sorted_members(struct_index); } @@ -1113,11 +1206,33 @@ namespace eos { namespace types { traversal_shortcut operator()(struct_type t) { + if( tc.valid_tuple_start_indices.find(t.index) != tc.valid_tuple_start_indices.end() ) + { + auto r = tc.tm.get_all_members(t.index); + if( r.end() - r.begin() == 2 ) + os << "Pair<"; + else + os << "Tuple<"; + + bool first = true; + for( auto itr = r.begin(); itr != r.end(); ++itr ) + { + if( first ) + first = false; + else + os << ", "; + tc.tm.traverse_type(itr->get_type_id(), *this); + } + + os << ">"; + return types_manager::no_deeper; + } + auto res = struct_index_map.emplace(t.index, struct_indices.size()); if( res.second ) struct_indices.push_back(t.index); os << "T" << (res.first->second + template_start_index); - return types_manager::no_shortcut; + return types_manager::no_deeper; } traversal_shortcut operator()(variant_type t) @@ -1204,53 +1319,61 @@ namespace eos { namespace types { static const char* ascending = "ascending"; static const char* descending = "descending"; - auto itr = valid_struct_start_indices.find(tid.get_type_index()); + auto struct_index = tid.get_type_index(); + auto itr = valid_struct_start_indices.find(struct_index); if( itr == valid_struct_start_indices.end() ) - throw std::invalid_argument("Struct does not exist."); - - auto res = tm.get_base_info(itr->first); - bool base_exists = (res.first != type_id::type_index_limit); - auto base_itr = valid_struct_start_indices.end(); - if( base_exists ) { - base_itr = valid_struct_start_indices.find(res.first); - if( base_itr == valid_struct_start_indices.end() ) - throw std::runtime_error("Invariant failure: Base of valid struct does not exist."); - } + if( valid_tuple_start_indices.find(struct_index) == valid_tuple_start_indices.end() ) + throw std::invalid_argument("Struct or tuple does not exist."); - os << "struct "; - os << itr->second << " /* struct(" << itr->first << ") */"; - if( base_exists ) + // Otherwise it is a tuple and so it needs to be handled by the same code that handles non-struct types. + } + else { - os << " : "; - os << base_itr->second << " /* struct(" << base_itr->first << ") */"; - if( res.second != field_metadata::unsorted ) + auto res = tm.get_base_info(itr->first); + bool base_exists = (res.first != type_id::type_index_limit); + auto base_itr = valid_struct_start_indices.end(); + if( base_exists ) { - os << " // Base sorted in " << ((res.second == field_metadata::ascending) ? ascending : descending); - os << " order"; + base_itr = valid_struct_start_indices.find(res.first); + if( base_itr == valid_struct_start_indices.end() ) + throw std::runtime_error("Invariant failure: Base of valid struct does not exist."); } - } - os << std::endl << "{" << std::endl; - auto r = tm.get_all_members(itr->first); - uint32_t field_seq_num = 0; - for( auto itr = r.begin() + (base_exists ? 1 : 0); itr != r.end(); ++field_seq_num, ++itr) - { - os << tab; - template_start_index = print_type(os, itr->get_type_id(), template_start_index, true); - os << " f" << field_seq_num << ";"; - - auto so = itr->get_sort_order(); - if( so != field_metadata::unsorted ) - os << " // Sorted in " << (so == field_metadata::ascending ? ascending : descending) << " order"; - os << std::endl; - } - os << "};" << std::endl; + os << "struct "; + os << itr->second << " /* struct(" << itr->first << ") */"; + if( base_exists ) + { + os << " : "; + os << base_itr->second << " /* struct(" << base_itr->first << ") */"; + if( res.second != field_metadata::unsorted ) + { + os << " // Base sorted in " << ((res.second == field_metadata::ascending) ? ascending : descending); + os << " order"; + } + } + os << std::endl << "{" << std::endl; - return template_start_index; + auto r = tm.get_all_members(itr->first); + uint32_t field_seq_num = 0; + for( auto itr = r.begin() + (base_exists ? 1 : 0); itr != r.end(); ++field_seq_num, ++itr) + { + os << tab; + template_start_index = print_type(os, itr->get_type_id(), template_start_index, true); + os << " f" << field_seq_num << ";"; + + auto so = itr->get_sort_order(); + if( so != field_metadata::unsorted ) + os << " // Sorted in " << (so == field_metadata::ascending ? ascending : descending) << " order"; + os << std::endl; + } + os << "};" << std::endl; + + return template_start_index; + } } - if( !is_type_valid(tid) ) + if( !tid.is_void() && !is_type_valid(tid) ) throw std::logic_error("Type is not valid."); // Else print the non-struct type in typical field format: diff --git a/programs/reflection_test1.cpp b/programs/reflection_test1.cpp index e5fe8a1..c4855b6 100644 --- a/programs/reflection_test1.cpp +++ b/programs/reflection_test1.cpp @@ -73,7 +73,6 @@ int main() vector abi_structs = { abi_tid.get_type_index(), abi_tc.get_struct_index(), abi_tc.get_struct_index(), - abi_tc.get_struct_index(), abi_tc.get_struct_index(), abi_tc.get_struct_index() };