diff --git a/include/boost/json/detail/value_to.hpp b/include/boost/json/detail/value_to.hpp index b63075bd..ac312d27 100644 --- a/include/boost/json/detail/value_to.hpp +++ b/include/boost/json/detail/value_to.hpp @@ -465,6 +465,18 @@ value_to_impl( *arr, boost::mp11::make_index_sequence()); } +template< class T> +struct is_optional + : std::false_type +{ }; + +#ifndef BOOST_NO_CXX17_HDR_OPTIONAL +template< class T> +struct is_optional< std::optional > + : std::true_type +{ }; +#endif // BOOST_NO_CXX17_HDR_OPTIONAL + template< class T > struct to_described_member { @@ -477,28 +489,44 @@ struct to_described_member result& res; object const& obj; + std::size_t count; template< class I > void - operator()(I) const + operator()(I) { if( !res ) return; using D = mp11::mp_at; + using M = described_member_t; + auto const found = obj.find(D::name); if( found == obj.end() ) { - error_code ec; - BOOST_JSON_FAIL(ec, error::unknown_name); - res = {boost::system::in_place_error, ec}; + BOOST_IF_CONSTEXPR( !is_optional::value ) + { + error_code ec; + BOOST_JSON_FAIL(ec, error::unknown_name); + res = {boost::system::in_place_error, ec}; + } return; } - using M = described_member_t; +#if defined(__GNUC__) && BOOST_GCC_VERSION >= 80000 && BOOST_GCC_VERSION < 11000 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused" +# pragma GCC diagnostic ignored "-Wunused-variable" +#endif auto member_res = try_value_to(found->value()); +#if defined(__GNUC__) && BOOST_GCC_VERSION >= 80000 && BOOST_GCC_VERSION < 11000 +# 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()}; } @@ -523,11 +551,16 @@ value_to_impl( return res; } - to_described_member member_converter{res, *obj}; - using Ds = typename decltype(member_converter)::Ds; + to_described_member member_converter{res, *obj, 0u}; + using Ds = typename decltype(member_converter)::Ds; constexpr std::size_t N = mp11::mp_size::value; - if( obj->size() != N ) + mp11::mp_for_each< mp11::mp_iota_c >(member_converter); + + if( !res ) + return res; + + if( member_converter.count != obj->size() ) { error_code ec; BOOST_JSON_FAIL(ec, error::size_mismatch); @@ -535,7 +568,6 @@ value_to_impl( return res; } - mp11::mp_for_each< mp11::mp_iota_c >(member_converter); return res; } diff --git a/test/value_to.cpp b/test/value_to.cpp index aa7f86ab..f2d79891 100644 --- a/test/value_to.cpp +++ b/test/value_to.cpp @@ -110,6 +110,20 @@ BOOST_DESCRIBE_STRUCT(T7, (T6), (s)) BOOST_DEFINE_ENUM_CLASS(E1, a, b, c) +//---------------------------------------------------------- + +struct T8 +{ + int n; + double d; +#ifndef BOOST_NO_CXX17_HDR_OPTIONAL + std::optional opt_s; +#else + std::string opt_s; +#endif // BOOST_NO_CXX17_HDR_OPTIONAL +}; +BOOST_DESCRIBE_STRUCT(T8, (), (n, d, opt_s)) + } // namespace value_to_test_ns namespace std @@ -318,6 +332,10 @@ public: BOOST_TEST( res ); BOOST_TEST( res->n == -78 ); BOOST_TEST( res->d == 0.125 ); + + jv.as_object()["x"] = 0; + BOOST_TEST_THROWS_WITH_LOCATION( + value_to<::value_to_test_ns::T6>( jv )); } { value jv = {{"n", 1}, {"d", 2}, {"s", "xyz"}}; @@ -349,7 +367,22 @@ public: value_to<::value_to_test_ns::E1>( value(1) )); BOOST_TEST_THROWS_WITH_LOCATION( value_to<::value_to_test_ns::E1>( value("x") )); -#endif + + { +#ifndef BOOST_NO_CXX17_HDR_OPTIONAL + value jv = {{"n", -78}, {"d", 0.125}}; + auto res = try_value_to<::value_to_test_ns::T8>(jv); + BOOST_TEST( res ); + BOOST_TEST( res->n == -78 ); + BOOST_TEST( res->d == 0.125 ); + 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 )); +#endif // BOOST_NO_CXX17_HDR_OPTIONAL + } +#endif // BOOST_DESCRIBE_CXX14 } #ifndef BOOST_NO_CXX17_HDR_OPTIONAL