From a86add9b283c0f3750d524b4aef23755ef264d37 Mon Sep 17 00:00:00 2001 From: Dmitry Arkhipov Date: Wed, 16 Oct 2024 20:47:56 +0300 Subject: [PATCH] allow extra members for converted structs --- include/boost/json/detail/parse_into.hpp | 207 ++++++++++++++++++----- include/boost/json/detail/value_to.hpp | 23 +-- test/parse_into.cpp | 23 ++- test/value_to.cpp | 10 +- 4 files changed, 193 insertions(+), 70 deletions(-) diff --git a/include/boost/json/detail/parse_into.hpp b/include/boost/json/detail/parse_into.hpp index 8349d4ca5..9e0f11ca7 100644 --- a/include/boost/json/detail/parse_into.hpp +++ b/include/boost/json/detail/parse_into.hpp @@ -711,7 +711,7 @@ struct handler_tuple; template< class P, template class L, class... V, std::size_t... I > struct handler_tuple< P, L, mp11::index_sequence > - : handler_tuple_element< I, get_handler > + : handler_tuple_element ... { handler_tuple( handler_tuple const& ) = delete; @@ -719,8 +719,8 @@ struct handler_tuple< P, L, mp11::index_sequence > template< class Access, class T > handler_tuple( Access access, T* pv, P* pp ) - : handler_tuple_element< I, get_handler >( - access( pv, mp11::mp_int() ), + : handler_tuple_element( + access( pv, mp11::mp_size_t() ), pp ) ... { } @@ -803,10 +803,17 @@ class converting_handler { private: + using ElementTypes = tuple_element_list; + + template + using ElementHandler = get_handler; + using InnerHandlers = mp11::mp_transform; + using HandlerTuple = handler_tuple; + T* value_; P* parent_; - handler_tuple< converting_handler, tuple_element_list > handlers_; + HandlerTuple handlers_; int inner_active_ = -1; public: @@ -874,7 +881,7 @@ class converting_handler struct do_on_array_begin { - handler_tuple< converting_handler, tuple_element_list >& handlers; + HandlerTuple& handlers; system::error_code& ec; template< class I > @@ -905,7 +912,7 @@ class converting_handler struct do_on_array_end { - handler_tuple< converting_handler, tuple_element_list >& handlers; + HandlerTuple& handlers; system::error_code& ec; template< class I > @@ -966,6 +973,13 @@ using struct_element_list = mp11::mp_transform_q< struct struct_accessor { + template< class T > + auto operator()( T*, mp11::mp_size< described_members > ) const + -> void* + { + return nullptr; + } + template< class T, class I > auto operator()( T* t, I ) const -> described_member_t, I> >* @@ -976,16 +990,132 @@ struct struct_accessor } }; -template< class F > struct struct_key_searcher { - F fn; + string_view key; + int& found; + int index = 0; + + struct_key_searcher(string_view key, int& found) noexcept + : key(key), found(found) + {} template< class D > void - operator()( D ) const + operator()( D ) + { + if( key == D::name ) + found = index; + ++index; + } +}; + +template +struct ignoring_handler +{ + P* parent_; + std::size_t array_depth_ = 0; + std::size_t object_depth_ = 0; + + ignoring_handler(ignoring_handler const&) = delete; + ignoring_handler& operator=(ignoring_handler const&) = delete; + + ignoring_handler(void*, P* p) noexcept + : parent_(p) + {} + + bool on_object_begin(system::error_code&) + { + ++object_depth_; + return true; + } + + bool on_object_end(system::error_code& ec) + { + BOOST_ASSERT( object_depth_ > 0 ); + --object_depth_; + + if( (array_depth_ + object_depth_) == 0 ) + return parent_->signal_value(ec); + return true; + } + + bool on_array_begin(system::error_code&) + { + ++array_depth_; + return true; + } + + bool on_array_end(system::error_code& ec) + { + BOOST_ASSERT( array_depth_ > 0 ); + --array_depth_; + + if( (array_depth_ + object_depth_) == 0 ) + return parent_->signal_end(ec); + return true; + } + + bool on_key_part(system::error_code&, string_view) + { + return true; + } + + bool on_key(system::error_code&, string_view) + { + return true; + } + + bool on_string_part(system::error_code&, string_view) + { + return true; + } + + bool on_string(system::error_code& ec, string_view) { - fn( D::name ) ; + if( (array_depth_ + object_depth_) == 0 ) + return parent_->signal_value(ec); + return true; + } + + bool on_number_part(system::error_code&) + { + return true; + } + + bool on_int64(system::error_code& ec, std::int64_t) + { + if( (array_depth_ + object_depth_) == 0 ) + return parent_->signal_value(ec); + return true; + } + + bool on_uint64(system::error_code& ec, std::uint64_t) + { + if( (array_depth_ + object_depth_) == 0 ) + return parent_->signal_value(ec); + return true; + } + + bool on_double(system::error_code& ec, double) + { + if( (array_depth_ + object_depth_) == 0 ) + return parent_->signal_value(ec); + return true; + } + + bool on_bool(system::error_code& ec, bool) + { + if( (array_depth_ + object_depth_) == 0 ) + return parent_->signal_value(ec); + return true; + } + + bool on_null(system::error_code& ec) + { + if( (array_depth_ + object_depth_) == 0 ) + return parent_->signal_value(ec); + return true; } }; @@ -1000,14 +1130,22 @@ class converting_handler #else private: + using Dm = described_members; + using Dt = struct_element_list; + + template + using MemberHandler = get_handler; + using InnerHandlers = mp11::mp_push_back< + mp11::mp_transform, + ignoring_handler >; + using InnerCount = mp11::mp_size; + V* value_; P* parent_; std::string key_; - using Dm = described_members; - - handler_tuple< converting_handler, struct_element_list > handlers_; + handler_tuple handlers_; int inner_active_ = -1; std::size_t activated_ = 0; @@ -1019,13 +1157,17 @@ class converting_handler : value_(v), parent_(p), handlers_(struct_accessor(), v, this) {} - struct is_optional_checker + struct is_required_checker { + bool operator()( mp11::mp_size
) const noexcept + { + return false; + } + template< class I > - bool operator()( I ) const noexcept + auto operator()( I ) const noexcept { - using L = struct_element_list; - using T = mp11::mp_at; + using T = mp11::mp_at; return !is_optional_like::value; } }; @@ -1033,9 +1175,9 @@ class converting_handler bool signal_value(system::error_code&) { BOOST_ASSERT( inner_active_ >= 0 ); - bool required_member = mp11::mp_with_index< mp11::mp_size >( + bool required_member = mp11::mp_with_index( inner_active_, - is_optional_checker{}); + is_required_checker{}); if( required_member ) ++activated_; @@ -1060,7 +1202,7 @@ class converting_handler auto f = [&](auto& handler) { return handler.fn ; }; \ using F = decltype(f); \ using H = decltype(handlers_); \ - return mp11::mp_with_index< mp11::mp_size >( \ + return mp11::mp_with_index( \ inner_active_, \ tuple_handler_op_invoker{handlers_, f} ); @@ -1076,9 +1218,8 @@ class converting_handler { if( inner_active_ < 0 ) { - using L = struct_element_list; - using C = mp11::mp_count_if; - constexpr int N = mp11::mp_size::value - C::value; + using C = mp11::mp_count_if; + constexpr int N = mp11::mp_size
::value - C::value; if( activated_ < N ) { BOOST_JSON_FAIL( ec, error::size_mismatch ); @@ -1129,24 +1270,8 @@ class converting_handler key = key_; } - int i = 0; - - auto f = [&](char const* name) - { - if( key == name ) - inner_active_ = i; - ++i; - }; - - mp11::mp_for_each( - struct_key_searcher{f} ); - - if( inner_active_ < 0 ) - { - BOOST_JSON_FAIL(ec, error::unknown_name); - return false; - } - + inner_active_ = InnerCount::value - 1; + mp11::mp_for_each( struct_key_searcher(key, inner_active_) ); return true; } diff --git a/include/boost/json/detail/value_to.hpp b/include/boost/json/detail/value_to.hpp index 0f95cdff0..054f08861 100644 --- a/include/boost/json/detail/value_to.hpp +++ b/include/boost/json/detail/value_to.hpp @@ -346,17 +346,13 @@ value_to_impl( *arr, ctx, boost::mp11::make_index_sequence()); } -template< class Ctx, class T, bool non_throwing = true > +template< class Ctx, class T > struct to_described_member { using Ds = described_members; - using result_type = mp11::mp_eval_if_c< - !non_throwing, T, system::result, T>; - - result_type& res; + system::result& res; object const& obj; - std::size_t count; Ctx const& ctx; template< class I > @@ -375,7 +371,7 @@ struct to_described_member BOOST_IF_CONSTEXPR( !is_optional_like::value ) { system::error_code ec; - BOOST_JSON_FAIL(ec, error::unknown_name); + BOOST_JSON_FAIL(ec, error::size_mismatch); res = {boost::system::in_place_error, ec}; } return; @@ -391,10 +387,7 @@ struct to_described_member # pragma GCC diagnostic pop #endif if( member_res ) - { (*res).* D::pointer = std::move(*member_res); - ++count; - } else res = {boost::system::in_place_error, member_res.error()}; } @@ -421,7 +414,7 @@ value_to_impl( return res; } - to_described_member< Ctx, T > member_converter{ res, *obj, 0u, ctx }; + to_described_member member_converter{res, *obj, ctx}; using Ds = typename decltype(member_converter)::Ds; constexpr std::size_t N = mp11::mp_size::value; @@ -430,14 +423,6 @@ value_to_impl( if( !res ) return res; - if( member_converter.count != obj->size() ) - { - system::error_code ec; - BOOST_JSON_FAIL(ec, error::size_mismatch); - res = {boost::system::in_place_error, ec}; - return res; - } - return res; } diff --git a/test/parse_into.cpp b/test/parse_into.cpp index c3c94b6f5..1fb674a0d 100644 --- a/test/parse_into.cpp +++ b/test/parse_into.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -86,14 +87,16 @@ class parse_into_test { public: - template void testParseInto( T const& t ) + template + void testParseIntoValue( value const& jv ) { #if defined(__GNUC__) && __GNUC__ < 5 # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wmissing-field-initializers" #endif - T t1( t ); - std::string json = serialize( value_from( t1 ) ); + T t1 = value_to(jv); + (void)t1; // older GCC thinks t1 can be unused + std::string json = serialize(jv); T t2{}; system::error_code jec; @@ -148,6 +151,12 @@ class parse_into_test #endif } + template + void testParseInto( T const& t ) + { + testParseIntoValue( value_from(t) ); + } + template void testParseIntoErrors( error e, value const& sample ) @@ -372,6 +381,11 @@ class parse_into_test #if defined(BOOST_DESCRIBE_CXX14) testParseInto( {} ); testParseInto( { 1, 3.14f, "hello" } ); + testParseIntoValue( { {"a", 1}, {"b", 3.14f}, {"c", "hello"}, {"d", 0} } ); + testParseIntoValue( { {"a", 1}, {"b", 3.14f}, {"c", "hello"}, {"d", array{} } } ); + testParseIntoValue( { {"a", 1}, {"b", 3.14f}, {"c", "hello"}, {"d", {1,2,3} } } ); + testParseIntoValue( { {"a", 1}, {"b", 3.14f}, {"c", "hello"}, {"d", object{} } } ); + testParseIntoValue( { {"a", 1}, {"b", 3.14f}, {"c", "hello"}, {"d", { {"a", "b"} } } } ); testParseInto( {} ); testParseInto( { { { 1, 1.0f, "one" }, { 2, 2.0f, "two" } }, { { "one", { 1, 1.1f, "1" } }, { "two", { 2, 2.2f, "2" } } } } ); @@ -380,9 +394,6 @@ class parse_into_test testParseInto( { {1, 3.14f, "hello"}, true } ); testParseIntoErrors( error::not_object, 1 ); - testParseIntoErrors( - error::unknown_name, - { {"a", 1}, {"b", 3.14f}, {"c", "hello"}, {"d", 0} } ); testParseIntoErrors( error::size_mismatch, { {"a", 1} } ); #endif } diff --git a/test/value_to.cpp b/test/value_to.cpp index 8ba18ac60..3c579ed34 100644 --- a/test/value_to.cpp +++ b/test/value_to.cpp @@ -432,8 +432,9 @@ class value_to_test BOOST_TEST( res->d == 0.125 ); jv.as_object()["x"] = 0; - BOOST_TEST_THROWS_WITH_LOCATION( - value_to<::value_to_test_ns::T6>( jv )); + res = try_value_to<::value_to_test_ns::T6>( + jv, ctx... ); + BOOST_TEST( res ); } { value jv = {{"n", 1}, {"d", 2}, {"s", "xyz"}, {"b", true}}; @@ -480,8 +481,9 @@ class value_to_test BOOST_TEST( std::nullopt == res->opt_s ); jv.as_object()["x"] = 0; - BOOST_TEST_THROWS_WITH_LOCATION( - value_to<::value_to_test_ns::T8>( jv, ctx... )); + res = try_value_to<::value_to_test_ns::T8>( + jv, ctx... ); + BOOST_TEST( res ); #endif // BOOST_NO_CXX17_HDR_OPTIONAL }