diff --git a/include/nlohmann/adl_serializer.hpp b/include/nlohmann/adl_serializer.hpp index 4af1c4bb1..8200c2809 100644 --- a/include/nlohmann/adl_serializer.hpp +++ b/include/nlohmann/adl_serializer.hpp @@ -1,14 +1,16 @@ #pragma once +#include #include #include #include +#include namespace nlohmann { -template +template struct adl_serializer { /*! @@ -20,14 +22,22 @@ struct adl_serializer @param[in] j JSON value to read from @param[in,out] val value to write to */ - template - static auto from_json(BasicJsonType&& j, ValueType& val) noexcept( + template + static auto from_json(BasicJsonType && j, U& val) noexcept( noexcept(::nlohmann::from_json(std::forward(j), val))) -> decltype(::nlohmann::from_json(std::forward(j), val), void()) { ::nlohmann::from_json(std::forward(j), val); } + template + static auto from_json(BasicJsonType && j) noexcept( + noexcept(::nlohmann::from_json(std::forward(j), detail::tag {}))) + -> decltype(::nlohmann::from_json(std::forward(j), detail::tag {})) + { + return ::nlohmann::from_json(std::forward(j), detail::tag {}); + } + /*! @brief convert any value type to a JSON value @@ -37,13 +47,12 @@ struct adl_serializer @param[in,out] j JSON value to write to @param[in] val value to read from */ - template - static auto to_json(BasicJsonType& j, ValueType&& val) noexcept( - noexcept(::nlohmann::to_json(j, std::forward(val)))) - -> decltype(::nlohmann::to_json(j, std::forward(val)), void()) + template + static auto to_json(BasicJsonType& j, U && val) noexcept( + noexcept(::nlohmann::to_json(j, std::forward(val)))) + -> decltype(::nlohmann::to_json(j, std::forward(val)), void()) { - ::nlohmann::to_json(j, std::forward(val)); + ::nlohmann::to_json(j, std::forward(val)); } }; - } // namespace nlohmann diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index 438b84a2e..557faa560 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -248,6 +249,27 @@ void()) from_json_array_impl(j, arr, priority_tag<3> {}); } +template < typename BasicJsonType, typename Array, std::size_t... Is > +Array from_json_array_impl(BasicJsonType&& j, tag /*unused*/, index_sequence /*unused*/) +{ + return { std::forward(j).at(Is).template get()... }; +} + +template < typename BasicJsonType, typename T, std::size_t N, + enable_if_t < !std::is_default_constructible>::value, int > = 0 > +auto from_json(BasicJsonType && j, tag> t) +-> decltype(j.template get(), +from_json_array_impl(std::forward(j), t, make_index_sequence {})) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + + std::string(j.type_name()))); + } + + return from_json_array_impl(std::forward(j), t, make_index_sequence {}); +} + template void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin) { @@ -323,22 +345,71 @@ void from_json(const BasicJsonType& j, ArithmeticType& val) } } -template -void from_json(const BasicJsonType& j, std::pair& p) +template>::value, int> = 0> +void from_json(BasicJsonType && j, std::pair& p) { - p = {j.at(0).template get(), j.at(1).template get()}; + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + + std::string(j.type_name()))); + } + + p = {std::forward(j).at(0).template get(), + std::forward(j).at(1).template get() + }; +} + +template < typename BasicJsonType, class A1, class A2, + enable_if_t < !std::is_default_constructible>::value, int > = 0 > +std::pair from_json(BasicJsonType && j, tag> /*unused*/) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + + std::string(j.type_name()))); + } + + return {std::forward(j).at(0).template get(), + std::forward(j).at(1).template get()}; } template -void from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence /*unused*/) +void from_json_tuple_impl(BasicJsonType&& j, Tuple& t, index_sequence /*unused*/) { - t = std::make_tuple(j.at(Idx).template get::type>()...); + t = std::make_tuple(std::forward(j).at(Idx).template get::type>()...); } -template -void from_json(const BasicJsonType& j, std::tuple& t) +template>::value, int > = 0 > +void from_json(BasicJsonType && j, std::tuple& t) { - from_json_tuple_impl(j, t, index_sequence_for {}); + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + + std::string(j.type_name()))); + } + + from_json_tuple_impl(std::forward(j), t, index_sequence_for {}); +} + +template +Tuple from_json_tuple_impl(BasicJsonType&& j, tag /*unused*/, index_sequence /*unused*/) +{ + return std::make_tuple(std::forward(j).at(Idx).template get::type>()...); +} + +template < typename BasicJsonType, typename... Args, + enable_if_t < !std::is_default_constructible>::value, int > = 0 > +std::tuple from_json(BasicJsonType && j, tag> t) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + + std::string(j.type_name()))); + } + + return from_json_tuple_impl(std::forward(j), t, index_sequence_for {}); } template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator, @@ -390,6 +461,14 @@ struct from_json_fn { return from_json(j, val); } + + template + auto operator()(const BasicJsonType& j, detail::tag t) const + noexcept(noexcept(from_json(j, t))) + -> decltype(from_json(j, t)) + { + return from_json(j, t); + } }; } // namespace detail diff --git a/include/nlohmann/detail/meta/tag.hpp b/include/nlohmann/detail/meta/tag.hpp new file mode 100644 index 000000000..631887d1d --- /dev/null +++ b/include/nlohmann/detail/meta/tag.hpp @@ -0,0 +1,10 @@ +#pragma once + +namespace nlohmann +{ +namespace detail +{ +// dispatching helper struct +template struct tag {}; +} // namespace detail +} // namespace nlohmann diff --git a/include/nlohmann/detail/meta/type_traits.hpp b/include/nlohmann/detail/meta/type_traits.hpp index 1706cbdc6..e30d99e4c 100644 --- a/include/nlohmann/detail/meta/type_traits.hpp +++ b/include/nlohmann/detail/meta/type_traits.hpp @@ -106,8 +106,7 @@ struct is_getable }; template -struct has_from_json < BasicJsonType, T, - enable_if_t < !is_basic_json::value >> +struct has_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> { using serializer = typename BasicJsonType::template json_serializer; diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 492118a5f..fd2811503 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -49,6 +49,7 @@ SOFTWARE. // #include +#include #include // #include @@ -2812,6 +2813,18 @@ constexpr T static_const::value; } // namespace detail } // namespace nlohmann +// #include + + +namespace nlohmann +{ +namespace detail +{ +// dispatching helper struct +template struct tag {}; +} // namespace detail +} // namespace nlohmann + // #include @@ -3129,8 +3142,7 @@ struct is_getable }; template -struct has_from_json < BasicJsonType, T, - enable_if_t < !is_basic_json::value >> +struct has_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> { using serializer = typename BasicJsonType::template json_serializer; @@ -3734,6 +3746,27 @@ void()) from_json_array_impl(j, arr, priority_tag<3> {}); } +template < typename BasicJsonType, typename Array, std::size_t... Is > +Array from_json_array_impl(BasicJsonType&& j, tag /*unused*/, index_sequence /*unused*/) +{ + return { std::forward(j).at(Is).template get()... }; +} + +template < typename BasicJsonType, typename T, std::size_t N, + enable_if_t < !std::is_default_constructible>::value, int > = 0 > +auto from_json(BasicJsonType && j, tag> t) +-> decltype(j.template get(), +from_json_array_impl(std::forward(j), t, make_index_sequence {})) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + + std::string(j.type_name()))); + } + + return from_json_array_impl(std::forward(j), t, make_index_sequence {}); +} + template void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin) { @@ -3809,22 +3842,71 @@ void from_json(const BasicJsonType& j, ArithmeticType& val) } } -template -void from_json(const BasicJsonType& j, std::pair& p) +template>::value, int> = 0> +void from_json(BasicJsonType && j, std::pair& p) { - p = {j.at(0).template get(), j.at(1).template get()}; + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + + std::string(j.type_name()))); + } + + p = {std::forward(j).at(0).template get(), + std::forward(j).at(1).template get() + }; +} + +template < typename BasicJsonType, class A1, class A2, + enable_if_t < !std::is_default_constructible>::value, int > = 0 > +std::pair from_json(BasicJsonType && j, tag> /*unused*/) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + + std::string(j.type_name()))); + } + + return {std::forward(j).at(0).template get(), + std::forward(j).at(1).template get()}; } template -void from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence /*unused*/) +void from_json_tuple_impl(BasicJsonType&& j, Tuple& t, index_sequence /*unused*/) { - t = std::make_tuple(j.at(Idx).template get::type>()...); + t = std::make_tuple(std::forward(j).at(Idx).template get::type>()...); } -template -void from_json(const BasicJsonType& j, std::tuple& t) +template>::value, int > = 0 > +void from_json(BasicJsonType && j, std::tuple& t) { - from_json_tuple_impl(j, t, index_sequence_for {}); + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + + std::string(j.type_name()))); + } + + from_json_tuple_impl(std::forward(j), t, index_sequence_for {}); +} + +template +Tuple from_json_tuple_impl(BasicJsonType&& j, tag /*unused*/, index_sequence /*unused*/) +{ + return std::make_tuple(std::forward(j).at(Idx).template get::type>()...); +} + +template < typename BasicJsonType, typename... Args, + enable_if_t < !std::is_default_constructible>::value, int > = 0 > +std::tuple from_json(BasicJsonType && j, tag> t) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + + std::string(j.type_name()))); + } + + return from_json_tuple_impl(std::forward(j), t, index_sequence_for {}); } template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator, @@ -3876,6 +3958,14 @@ struct from_json_fn { return from_json(j, val); } + + template + auto operator()(const BasicJsonType& j, detail::tag t) const + noexcept(noexcept(from_json(j, t))) + -> decltype(from_json(j, t)) + { + return from_json(j, t); + } }; } // namespace detail @@ -4448,11 +4538,13 @@ constexpr const auto& to_json = detail::static_const::value; } // namespace } // namespace nlohmann +// #include + namespace nlohmann { -template +template struct adl_serializer { /*! @@ -4464,14 +4556,22 @@ struct adl_serializer @param[in] j JSON value to read from @param[in,out] val value to write to */ - template - static auto from_json(BasicJsonType&& j, ValueType& val) noexcept( + template + static auto from_json(BasicJsonType && j, U& val) noexcept( noexcept(::nlohmann::from_json(std::forward(j), val))) -> decltype(::nlohmann::from_json(std::forward(j), val), void()) { ::nlohmann::from_json(std::forward(j), val); } + template + static auto from_json(BasicJsonType && j) noexcept( + noexcept(::nlohmann::from_json(std::forward(j), detail::tag {}))) + -> decltype(::nlohmann::from_json(std::forward(j), detail::tag {})) + { + return ::nlohmann::from_json(std::forward(j), detail::tag {}); + } + /*! @brief convert any value type to a JSON value @@ -4481,15 +4581,14 @@ struct adl_serializer @param[in,out] j JSON value to write to @param[in] val value to read from */ - template - static auto to_json(BasicJsonType& j, ValueType&& val) noexcept( - noexcept(::nlohmann::to_json(j, std::forward(val)))) - -> decltype(::nlohmann::to_json(j, std::forward(val)), void()) + template + static auto to_json(BasicJsonType& j, U && val) noexcept( + noexcept(::nlohmann::to_json(j, std::forward(val)))) + -> decltype(::nlohmann::to_json(j, std::forward(val)), void()) { - ::nlohmann::to_json(j, std::forward(val)); + ::nlohmann::to_json(j, std::forward(val)); } }; - } // namespace nlohmann // #include diff --git a/test/src/unit-regression2.cpp b/test/src/unit-regression2.cpp index 1e8c4922a..e2adff862 100644 --- a/test/src/unit-regression2.cpp +++ b/test/src/unit-regression2.cpp @@ -115,7 +115,7 @@ namespace nlohmann template <> struct adl_serializer { - static NonDefaultFromJsonStruct from_json (json const&) noexcept + static NonDefaultFromJsonStruct from_json (const json&) noexcept { return {}; } @@ -133,6 +133,28 @@ struct NotSerializableData }; +///////////////////////////////////////////////////////////////////// +// for #2574 +///////////////////////////////////////////////////////////////////// +struct NonDefaultConstructible +{ + explicit NonDefaultConstructible (int x) : x(x) { } + int x; +}; + +namespace nlohmann +{ +template <> +struct adl_serializer +{ + static NonDefaultConstructible from_json (const json& j) noexcept + { + return NonDefaultConstructible(j.get()); + } +}; +} + + TEST_CASE("regression tests 2") { SECTION("issue #1001 - Fix memory leak during parser callback") @@ -498,4 +520,59 @@ TEST_CASE("regression tests 2") CHECK(j.dump() == "\"Hello, world!\""); } #endif + + SECTION("issue #2574 - Deserialization to std::array, std::pair, and std::tuple with non-default constructable types fails") + { + SECTION("std::array") + { + json j = { 7, 4 }; + auto arr = j.get>(); + CHECK(arr[0].x == 7); + CHECK(arr[1].x == 4); + } + + SECTION("std::pair") + { + { + json j = { 3, 8 }; + auto x = j.at(0).get(); + CHECK(x.x == 3); + + auto p = j.get>(); + CHECK(p.first.x == 3); + CHECK(p.second.x == 8); + } + + { + json j = { 4, 1 }; + auto p = j.get>(); + CHECK(p.first == 4); + CHECK(p.second.x == 1); + } + + { + json j = { 6, 7 }; + auto p = j.get>(); + CHECK(p.first.x == 6); + CHECK(p.second == 7); + } + } + + SECTION("std::tuple") + { + { + json j = { 9 }; + auto t = j.get>(); + CHECK(std::get<0>(t).x == 9); + } + + { + json j = { 9, 8, 7 }; + auto t = j.get>(); + CHECK(std::get<0>(t).x == 9); + CHECK(std::get<1>(t) == 8); + CHECK(std::get<2>(t).x == 7); + } + } + } }