diff --git a/include/boost/pfr/core.hpp b/include/boost/pfr/core.hpp index 58f6c49..3302cb8 100644 --- a/include/boost/pfr/core.hpp +++ b/include/boost/pfr/core.hpp @@ -171,6 +171,7 @@ struct size_array { // libc++ misses constexpr on operat static constexpr std::size_t size() noexcept { return N; } + constexpr std::size_t count_nonzeros() const noexcept { std::size_t count = 0; for (std::size_t i = 0; i < size(); ++i) { @@ -180,6 +181,28 @@ struct size_array { // libc++ misses constexpr on operat } return count; } + + constexpr std::size_t count_from_opening_till_matching_parenthis_seq(std::size_t from, std::size_t opening_parenthis, std::size_t closing_parenthis) const noexcept { + if (data[from] != opening_parenthis) { + return 0; + } + std::size_t unclosed_parnthesis = 0; + std::size_t count = 0; + for (; ; ++from) { + if (data[from] == opening_parenthis) { + ++ unclosed_parnthesis; + } else if (data[from] == closing_parenthis) { + -- unclosed_parnthesis; + } + ++ count; + + if (unclosed_parnthesis == 0) { + return count; + } + } + + return count; + } }; template <> @@ -201,7 +224,8 @@ constexpr std::size_t get(const size_array& a) noexcept { } -template constexpr size_array fields_count_and_type_ids_with_zeros() noexcept; +template constexpr size_array fields_count_and_type_ids_with_zeros() noexcept; +template constexpr auto flat_array_of_type_ids() noexcept; ///////////////////// All the stuff for representing Type as integer and converting integer back to type namespace typeid_conversions { @@ -268,7 +292,7 @@ template constexpr std::size_t type_to_id(identity) template constexpr std::size_t type_to_id(identity) noexcept; template constexpr std::size_t type_to_id(identity, std::enable_if_t::value>* = 0) noexcept; template constexpr std::size_t type_to_id(identity, std::enable_if_t::value>* = 0) noexcept; -template constexpr size_array type_to_id(identity, std::enable_if_t::value && !std::is_empty::value>* = 0) noexcept; +template constexpr size_array type_to_id(identity, std::enable_if_t::value && !std::is_empty::value>* = 0) noexcept; template constexpr auto id_to_type(size_t_, if_extension = 0) noexcept; template constexpr auto id_to_type(size_t_, if_extension = 0) noexcept; @@ -315,6 +339,8 @@ BOOST_MAGIC_GET_REGISTER_TYPE(const void* , 20) BOOST_MAGIC_GET_REGISTER_TYPE(volatile void* , 21) BOOST_MAGIC_GET_REGISTER_TYPE(const volatile void* , 22) BOOST_MAGIC_GET_REGISTER_TYPE(std::nullptr_t , 23) +constexpr std::size_t tuple_begin_tag = 24; +constexpr std::size_t tuple_end_tag = 25; #undef BOOST_MAGIC_GET_REGISTER_TYPE @@ -381,8 +407,22 @@ constexpr std::size_t type_to_id(identity, std::enable_if_t -constexpr size_array type_to_id(identity, std::enable_if_t::value && !std::is_empty::value>*) noexcept { - return fields_count_and_type_ids_with_zeros(); +constexpr size_array type_to_id(identity, std::enable_if_t::value && !std::is_empty::value>*) noexcept { + constexpr auto t = flat_array_of_type_ids(); + size_array result {{tuple_begin_tag}}; + constexpr bool requires_tuplening = ( + (t.count_nonzeros() == 1) || (t.count_nonzeros() == t.count_from_opening_till_matching_parenthis_seq(0, tuple_begin_tag, tuple_end_tag)) + ); + + if (requires_tuplening) { + for (std::size_t i = 0; i < t.size(); ++i) + result.data[i + 1] = t.data[i]; + result.data[result.size() - 1] = tuple_end_tag; + } else { + for (std::size_t i = 0; i < t.size(); ++i) + result.data[i] = t.data[i]; + } + return result; } @@ -553,7 +593,7 @@ constexpr auto flat_type_to_array_of_type_ids(std::size_t* types, std::index_seq ); constexpr auto offsets = get_type_offsets(); - T tmp{ ubiq_val{types + get(offsets)}... }; + T tmp{ ubiq_val{types + get(offsets) * 3}... }; (void)tmp; (void)offsets; // If type is empty offsets are not used return nullptr; @@ -602,8 +642,8 @@ constexpr std::size_t fields_count() noexcept { ///////////////////// Returns array of typeids and zeros template -constexpr size_array fields_count_and_type_ids_with_zeros() noexcept { - size_array types{}; +constexpr size_array fields_count_and_type_ids_with_zeros() noexcept { + size_array types{}; constexpr std::size_t N = fields_count(); flat_type_to_array_of_type_ids(types.data, std::make_index_sequence()); return types; @@ -627,15 +667,71 @@ constexpr auto flat_array_of_type_ids() noexcept { } ///////////////////// Convert array of typeids into sequence_tuple::tuple + +template +constexpr auto as_flat_tuple_impl(std::index_sequence) noexcept; + +template +constexpr auto increment_index_sequence(std::index_sequence) noexcept { + return std::index_sequence{}; +} + +template +constexpr auto prepare_subtuples(size_t_, size_t_) { + return typeid_conversions::id_to_type(size_t_{}); +} + +struct end_cleanup_tag{}; + +template +constexpr end_cleanup_tag prepare_subtuples(size_t_, size_t_) noexcept { + return end_cleanup_tag{}; +} + +template +constexpr auto prepare_subtuples(size_t_, size_t_) noexcept { + constexpr auto a = flat_array_of_type_ids(); + constexpr std::size_t subtuple_size = a.count_from_opening_till_matching_parenthis_seq(I, typeid_conversions::tuple_begin_tag, typeid_conversions::tuple_end_tag); + static_assert(subtuple_size > 2, "Internal error while representing nested field as tuple"); + constexpr auto seq = std::make_index_sequence{}; + return as_flat_tuple_impl( increment_index_sequence(seq) ); +} + + +template +constexpr auto make_array(std::index_sequence) noexcept { + return size_array{{I...}}; +} + +template +constexpr auto make_array(std::index_sequence, std::index_sequence, Others... vals) noexcept { + return make_array(std::index_sequence{}, vals...); +} + + + template constexpr auto as_flat_tuple_impl(std::index_sequence) noexcept { constexpr auto a = flat_array_of_type_ids(); (void)a; // `a` is unused if T is an empty type - return sequence_tuple::tuple< + + typedef sequence_tuple::tuple(size_t_< get(a) >{}, size_t_{}) + )...> subtuples_uncleanuped_t; + + /*constexpr auto skips = make_array( + increment_index_sequence( + std::make_index_sequence< + std::min(a.count_from_opening_till_matching_parenthis_seq(I, typeid_conversions::tuple_begin_tag, typeid_conversions::tuple_end_tag), std::size_t{1}) - 1 + >{} + )... + );*/ + + return subtuples_uncleanuped_t{}; /* sequence_tuple::tuple< decltype(typeid_conversions::id_to_type( size_t_(a)>{} ))... - >{}; + >{};*/ } template diff --git a/misc/generate_cpp17.py b/misc/generate_cpp17.py old mode 100755 new mode 100644 diff --git a/misc/generate_single.sh b/misc/generate_single.sh old mode 100755 new mode 100644 diff --git a/test/core.cpp b/test/core.cpp index b0cf322..df61272 100644 --- a/test/core.cpp +++ b/test/core.cpp @@ -14,6 +14,7 @@ #include #include #include +#include using namespace boost::pfr; @@ -121,13 +122,13 @@ constexpr void test_compiletime_array() { static_assert( std::is_same< decltype(flat_get<19>(f)), T const&>::value, "types missmatch"); } { - constexpr T f[2][10] = {0}; + constexpr T f[2][10] = {{0}}; static_assert(flat_tuple_size_v == 20, "failed tuple size check for array"); static_assert( std::is_same< decltype(flat_get<0>(f)), T const&>::value, "types missmatch"); static_assert( std::is_same< decltype(flat_get<19>(f)), T const&>::value, "types missmatch"); } { - constexpr T f[2][5][2] = {0}; + constexpr T f[2][5][2] = {{{0}}}; static_assert(flat_tuple_size_v == 20, "failed tuple size check for array"); static_assert( std::is_same< decltype(flat_get<0>(f)), T const&>::value, "types missmatch"); static_assert( std::is_same< decltype(flat_get<19>(f)), T const&>::value, "types missmatch"); @@ -330,6 +331,32 @@ void test_hash() { BOOST_TEST_NE(flat_hash()({199}), std::hash()(199)); } +// Test case by Lisa Lippincott +void test_alignment_with_neted_structure() { + struct A0 { + short s; + char c; + }; + + struct B0 { + A0 a; + char c1; + char c2; + }; + + B0 test_struct; + std::memset(&test_struct, 0, sizeof(test_struct)); + test_struct.a.s = 0; + test_struct.a.c = '1'; + test_struct.c1 = '2'; + test_struct.c2 = '3'; + BOOST_TEST_EQ(flat_get<0>(test_struct), 0); + BOOST_TEST_EQ(flat_get<1>(test_struct), '1'); + BOOST_TEST_EQ(flat_get<2>(test_struct), '2'); + BOOST_TEST_EQ(flat_get<3>(test_struct), '3'); + +} + int main() { test_compiletime(); test_compiletime_array(); @@ -398,6 +425,8 @@ int main() { static_assert(tuple_size::value == 4, ""); static_assert(flat_tuple_size::value == 4, ""); + test_alignment_with_neted_structure(); + return boost::report_errors(); }