diff --git a/include/boost/json/conversion.hpp b/include/boost/json/conversion.hpp index 1d30b3a29..74a6ca98b 100644 --- a/include/boost/json/conversion.hpp +++ b/include/boost/json/conversion.hpp @@ -12,6 +12,7 @@ #include #include +#include #include @@ -437,6 +438,27 @@ struct is_variant_like; template struct is_optional_like; +struct context_key_push_tag{}; +struct context_key_pop_tag{}; +struct context_index_push_tag{}; +struct context_index_pop_tag{}; + +template< class Ctx > +void +context_key_push( Ctx const& ctx, string_view sv ); + +template< class Ctx > +void +context_key_pop( Ctx const& ctx ); + +template< class Ctx > +void +context_index_push( Ctx const& ctx, std::size_t n ); + +template< class Ctx > +void +context_index_pop( Ctx const& ctx ); + } // namespace json } // namespace boost diff --git a/include/boost/json/detail/value_to.hpp b/include/boost/json/detail/value_to.hpp index 29ae444f9..0fede5fe9 100644 --- a/include/boost/json/detail/value_to.hpp +++ b/include/boost/json/detail/value_to.hpp @@ -232,12 +232,14 @@ value_to_impl( auto ins = detail::inserter(res, inserter_implementation()); for( key_value_pair const& kv: *obj ) { + json::context_key_push( ctx, kv.key() ); auto elem_res = try_value_to>( kv.value(), ctx ); if( elem_res.has_error() ) return {boost::system::in_place_error, elem_res.error()}; *ins++ = value_type{ key_type(kv.key()), std::move(*elem_res)}; + json::context_key_pop(ctx); } return res; } @@ -272,10 +274,12 @@ value_to_impl( auto ins = detail::inserter(result, inserter_implementation()); for( value const& val: *arr ) { + json::context_index_push( ctx, &val - arr->begin() ); auto elem_res = try_value_to>( val, ctx ); if( elem_res.has_error() ) return {boost::system::in_place_error, elem_res.error()}; *ins++ = std::move(*elem_res); + json::context_index_pop(ctx); } return result; } @@ -283,13 +287,17 @@ value_to_impl( // tuple-like types template< class T, class Ctx > system::result -try_make_tuple_elem(value const& jv, Ctx const& ctx, system::error_code& ec) +try_make_tuple_elem( + value const* b, std::size_t n, Ctx const& ctx, system::error_code& ec) { if( ec.failed() ) return {boost::system::in_place_error, ec}; - auto result = try_value_to( jv, ctx ); + json::context_index_push(ctx, n); + auto result = try_value_to( b[n], ctx ); ec = result.error(); + if( !ec.failed() ) + json::context_index_pop(ctx); return result; } @@ -302,7 +310,7 @@ try_make_tuple_like( auto items = std::make_tuple( try_make_tuple_elem< typename std::decay>::type >( - arr[Is], ctx, ec) + arr.data(), Is, ctx, ec) ...); #if defined(BOOST_GCC) # pragma GCC diagnostic push @@ -377,6 +385,7 @@ struct to_described_member return; } + json::context_key_push(ctx, D::name); #if defined(__GNUC__) && BOOST_GCC_VERSION >= 80000 && BOOST_GCC_VERSION < 11000 # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wunused" @@ -387,9 +396,14 @@ struct to_described_member # pragma GCC diagnostic pop #endif if( member_res ) + { (*res).* D::pointer = std::move(*member_res); + json::context_key_pop(ctx); + } else + { res = {boost::system::in_place_error, member_res.error()}; + } } }; diff --git a/include/boost/json/impl/conversion.hpp b/include/boost/json/impl/conversion.hpp index 6c045ea01..18cde9a6f 100644 --- a/include/boost/json/impl/conversion.hpp +++ b/include/boost/json/impl/conversion.hpp @@ -563,6 +563,58 @@ struct is_optional_like mp11::mp_valid> { }; +inline +void +tag_invoke(context_key_push_tag, string_view, ...) +{ +} + +inline +void +tag_invoke(context_key_pop_tag, ...) +{ +} + +inline +void +tag_invoke(context_index_push_tag, std::size_t, ...) +{ +} + +inline +void +tag_invoke(context_index_pop_tag, ...) +{ +} + +template< class Ctx > +void +context_key_push(Ctx const& ctx, string_view sv) +{ + tag_invoke(context_key_push_tag(), sv, ctx); +} + +template< class Ctx > +void +context_key_pop(Ctx const& ctx) +{ + tag_invoke(context_key_pop_tag(), ctx); +} + +template< class Ctx > +void +context_index_push(Ctx const& ctx, std::size_t n) +{ + tag_invoke(context_index_push_tag(), n, ctx); +} + +template< class Ctx > +void +context_index_pop(Ctx const& ctx) +{ + tag_invoke(context_index_pop_tag(), ctx); +} + } // namespace json } // namespace boost diff --git a/test/value_to.cpp b/test/value_to.cpp index 5d6fcc34a..91b9c7d21 100644 --- a/test/value_to.cpp +++ b/test/value_to.cpp @@ -196,6 +196,45 @@ tag_invoke( return T11( jv.to_number() ); } +struct logging_context +{ + std::vector& path; +}; + +void +tag_invoke( + boost::json::context_key_push_tag, + boost::json::string_view sv, + logging_context const& ctx) +{ + ctx.path.push_back(sv); +} + +void +tag_invoke( + boost::json::context_key_pop_tag, + logging_context const& ctx) +{ + ctx.path.pop_back(); +} + +void +tag_invoke( + boost::json::context_index_push_tag, + std::size_t n, + logging_context const& ctx) +{ + ctx.path.push_back( std::to_string(n) ); +} + +void +tag_invoke( + boost::json::context_index_pop_tag, + logging_context const& ctx) +{ + ctx.path.pop_back(); +} + } // namespace value_to_test_ns namespace std @@ -814,6 +853,17 @@ class value_to_test testContext(); testContainerHelpers(); + + value jv = { + {{"n", 1}, {"d", 2}}, + {{"n", 3}, {"d", "4"}} + }; + std::vector error_path; + value_to_test_ns::logging_context ctx{error_path}; + auto result = try_value_to< std::vector >( + jv, ctx); + BOOST_TEST( result.error().failed() ); + BOOST_TEST(( std::vector{"1", "d"} == error_path )); } };