From 1e825e4f92b84bea316dbd935bfef0e1299b6ccf Mon Sep 17 00:00:00 2001 From: Anthony VH Date: Sat, 9 Jan 2021 00:08:27 +0100 Subject: [PATCH 01/17] Add support for deserialization of STL containers of non-default constructable types (fixes #2574). --- include/nlohmann/adl_serializer.hpp | 27 ++-- .../nlohmann/detail/conversions/from_json.hpp | 95 +++++++++++- include/nlohmann/detail/meta/tag.hpp | 10 ++ include/nlohmann/detail/meta/type_traits.hpp | 3 +- single_include/nlohmann/json.hpp | 137 +++++++++++++++--- test/src/unit-regression2.cpp | 79 +++++++++- 6 files changed, 312 insertions(+), 39 deletions(-) create mode 100644 include/nlohmann/detail/meta/tag.hpp diff --git a/include/nlohmann/adl_serializer.hpp b/include/nlohmann/adl_serializer.hpp index 4af1c4bb1d..8200c28099 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 438b84a2e1..557faa5601 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) +{ + 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(BasicJsonType&& j, Tuple& t, index_sequence /*unused*/) { - p = {j.at(0).template get(), j.at(1).template get()}; + t = std::make_tuple(std::forward(j).at(Idx).template get::type>()...); +} + +template>::value, int > = 0 > +void from_json(BasicJsonType && j, std::tuple& 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()))); + } + + from_json_tuple_impl(std::forward(j), t, index_sequence_for {}); } template -void from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence /*unused*/) +Tuple from_json_tuple_impl(BasicJsonType&& j, tag /*unused*/, index_sequence /*unused*/) { - t = std::make_tuple(j.at(Idx).template get::type>()...); + return std::make_tuple(std::forward(j).at(Idx).template get::type>()...); } -template -void from_json(const BasicJsonType& j, std::tuple& t) +template < typename BasicJsonType, typename... Args, + enable_if_t < !std::is_default_constructible>::value, int > = 0 > +std::tuple from_json(BasicJsonType && j, tag> 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()))); + } + + 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 0000000000..631887d1d2 --- /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 1706cbdc6a..e30d99e4ce 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 492118a5f8..fd28115030 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 1e8c4922a7..e2adff8623 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); + } + } + } } From c0a8b45bbb4c9d857f1672a6199c1e8659ea9cd2 Mon Sep 17 00:00:00 2001 From: Anthony VH Date: Sat, 9 Jan 2021 17:45:56 +0100 Subject: [PATCH 02/17] Renamed template parameter and added some comments. --- include/nlohmann/adl_serializer.hpp | 36 ++++++++++++------ .../nlohmann/detail/conversions/from_json.hpp | 2 + single_include/nlohmann/json.hpp | 38 +++++++++++++------ 3 files changed, 54 insertions(+), 22 deletions(-) diff --git a/include/nlohmann/adl_serializer.hpp b/include/nlohmann/adl_serializer.hpp index 8200c28099..9eb751b705 100644 --- a/include/nlohmann/adl_serializer.hpp +++ b/include/nlohmann/adl_serializer.hpp @@ -19,23 +19,37 @@ struct adl_serializer This function is usually called by the `get()` function of the @ref basic_json class (either explicit or via conversion operators). + @note This function is chosen for value types which can be default constructed. + @param[in] j JSON value to read from @param[in,out] val value to write to */ - template - static auto from_json(BasicJsonType && j, U& val) noexcept( + template + static auto from_json(BasicJsonType && j, TargetType& 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 + /*! + @brief convert a JSON value to any value type + + This function is usually called by the `get()` function of the + @ref basic_json class (either explicit or via conversion operators). + + @note This function is chosen for value types which can not be default constructed. + + @param[in] j JSON value to read from + + @return copy of the JSON value, converted to @a ValueType + */ + 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 {})) + 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 {}); + return ::nlohmann::from_json(std::forward(j), detail::tag {}); } /*! @@ -47,12 +61,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, U && 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, TargetType && 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 557faa5601..c9b88ce5b9 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -462,6 +462,8 @@ struct from_json_fn return from_json(j, val); } + // overload to pass calls to built-in from_json functions for non-default constructible STL + // types (e.g. std::array, where X is not default constructible). template auto operator()(const BasicJsonType& j, detail::tag t) const noexcept(noexcept(from_json(j, t))) diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index fd28115030..06f7caa378 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -3959,6 +3959,8 @@ struct from_json_fn return from_json(j, val); } + // overload to pass calls to built-in from_json functions for non-default constructible STL + // types (e.g. std::array, where X is not default constructible). template auto operator()(const BasicJsonType& j, detail::tag t) const noexcept(noexcept(from_json(j, t))) @@ -4553,23 +4555,37 @@ struct adl_serializer This function is usually called by the `get()` function of the @ref basic_json class (either explicit or via conversion operators). + @note This function is chosen for value types which can be default constructed. + @param[in] j JSON value to read from @param[in,out] val value to write to */ - template - static auto from_json(BasicJsonType && j, U& val) noexcept( + template + static auto from_json(BasicJsonType && j, TargetType& 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 + /*! + @brief convert a JSON value to any value type + + This function is usually called by the `get()` function of the + @ref basic_json class (either explicit or via conversion operators). + + @note This function is chosen for value types which can not be default constructed. + + @param[in] j JSON value to read from + + @return copy of the JSON value, converted to @a ValueType + */ + 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 {})) + 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 {}); + return ::nlohmann::from_json(std::forward(j), detail::tag {}); } /*! @@ -4581,12 +4597,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, U && 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, TargetType && 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 From 1b113f73c2c79e7bd2c659b153becba747007bb1 Mon Sep 17 00:00:00 2001 From: Anthony VH Date: Sat, 9 Jan 2021 17:54:56 +0100 Subject: [PATCH 03/17] Added extra tests to improve coverage. --- test/src/unit-regression2.cpp | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/test/src/unit-regression2.cpp b/test/src/unit-regression2.cpp index e2adff8623..1046bb2e58 100644 --- a/test/src/unit-regression2.cpp +++ b/test/src/unit-regression2.cpp @@ -525,10 +525,18 @@ TEST_CASE("regression tests 2") { SECTION("std::array") { - json j = { 7, 4 }; - auto arr = j.get>(); - CHECK(arr[0].x == 7); - CHECK(arr[1].x == 4); + { + json j = { 7, 4 }; + auto arr = j.get>(); + CHECK(arr[0].x == 7); + CHECK(arr[1].x == 4); + + } + + { + json j = 7; + CHECK_THROWS_AS((j.get>()), json::type_error); + } } SECTION("std::pair") @@ -556,6 +564,11 @@ TEST_CASE("regression tests 2") CHECK(p.first.x == 6); CHECK(p.second == 7); } + + { + json j = 7; + CHECK_THROWS_AS((j.get>()), json::type_error); + } } SECTION("std::tuple") @@ -573,6 +586,11 @@ TEST_CASE("regression tests 2") CHECK(std::get<1>(t) == 8); CHECK(std::get<2>(t).x == 7); } + + { + json j = 7; + CHECK_THROWS_AS((j.get>()), json::type_error); + } } } } From 23f462b598b9da7b30e2924af39f807c45deaa63 Mon Sep 17 00:00:00 2001 From: Anthony VH Date: Sun, 10 Jan 2021 19:23:32 +0100 Subject: [PATCH 04/17] Reduced code duplication, renamed tag to identity_tag. --- include/nlohmann/adl_serializer.hpp | 7 +- .../nlohmann/detail/conversions/from_json.hpp | 97 +++++++--------- .../detail/meta/{tag.hpp => identity_tag.hpp} | 2 +- single_include/nlohmann/json.hpp | 107 ++++++++---------- 4 files changed, 99 insertions(+), 114 deletions(-) rename include/nlohmann/detail/meta/{tag.hpp => identity_tag.hpp} (75%) diff --git a/include/nlohmann/adl_serializer.hpp b/include/nlohmann/adl_serializer.hpp index 9eb751b705..1dee29eb94 100644 --- a/include/nlohmann/adl_serializer.hpp +++ b/include/nlohmann/adl_serializer.hpp @@ -5,6 +5,7 @@ #include #include +#include #include namespace nlohmann @@ -46,10 +47,10 @@ struct adl_serializer */ 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 {})) + noexcept(::nlohmann::from_json(std::forward(j), detail::identity_tag {}))) + -> decltype(::nlohmann::from_json(std::forward(j), detail::identity_tag {})) { - return ::nlohmann::from_json(std::forward(j), detail::tag {}); + return ::nlohmann::from_json(std::forward(j), detail::identity_tag {}); } /*! diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index c9b88ce5b9..b29ac05c64 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include @@ -250,16 +250,16 @@ void()) } template < typename BasicJsonType, typename Array, std::size_t... Is > -Array from_json_array_impl(BasicJsonType&& j, tag /*unused*/, index_sequence /*unused*/) +Array from_json_array_impl(BasicJsonType&& j, identity_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) +auto from_json(BasicJsonType && j, identity_tag> tag) -> decltype(j.template get(), -from_json_array_impl(std::forward(j), t, make_index_sequence {})) +from_json_array_impl(std::forward(j), tag, make_index_sequence {})) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { @@ -267,7 +267,7 @@ from_json_array_impl(std::forward(j), t, make_index_sequence { std::string(j.type_name()))); } - return from_json_array_impl(std::forward(j), t, make_index_sequence {}); + return from_json_array_impl(std::forward(j), tag, make_index_sequence {}); } template @@ -345,24 +345,24 @@ void from_json(const BasicJsonType& j, ArithmeticType& val) } } +template < typename BasicJsonType, class A1, class A2 > +std::pair from_json_pair_impl(BasicJsonType&& j, identity_tag> /*unused*/, priority_tag<0> /*unused*/) +{ + return {std::forward(j).at(0).template get(), + std::forward(j).at(1).template get()}; +} + + template>::value, int> = 0> -void from_json(BasicJsonType && j, std::pair& p) +void from_json_pair_impl(BasicJsonType && j, std::pair& p, priority_tag<1> /*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()))); - } - - p = {std::forward(j).at(0).template get(), - std::forward(j).at(1).template get() - }; + p = from_json_pair_impl(std::forward(j), identity_tag> {}, priority_tag<0> {}); } -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*/) +template +auto from_json(BasicJsonType&& j, PairRelatedType&& p) +-> decltype(from_json_pair_impl(std::forward(j), std::forward(p), priority_tag<1> {})) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { @@ -370,19 +370,25 @@ std::pair from_json(BasicJsonType && j, tag> /*unused* std::string(j.type_name()))); } - return {std::forward(j).at(0).template get(), - std::forward(j).at(1).template get()}; + return from_json_pair_impl(std::forward(j), std::forward(p), priority_tag<1> {}); } template -void from_json_tuple_impl(BasicJsonType&& j, Tuple& t, index_sequence /*unused*/) +Tuple from_json_tuple_impl(BasicJsonType&& j, identity_tag /*unused*/, index_sequence /*unused*/, priority_tag<0> /*unused*/) +{ + return std::make_tuple(std::forward(j).at(Idx).template get::type>()...); +} + +template::value, int> = 0> +void from_json_tuple_impl(BasicJsonType && j, Tuple& t, index_sequence /*unused*/, priority_tag<1> /*unused*/) { - t = std::make_tuple(std::forward(j).at(Idx).template get::type>()...); + t = from_json_tuple_impl(std::forward(j), identity_tag {}, priority_tag<0> {}); } -template>::value, int > = 0 > -void from_json(BasicJsonType && j, std::tuple& t) +template +auto from_json_tuple(BasicJsonType&& j, TupleRelated&& t, index_sequence idx) +-> decltype(from_json_tuple_impl(std::forward(j), std::forward(t), idx, priority_tag<1> {})) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { @@ -390,26 +396,21 @@ void from_json(BasicJsonType && j, std::tuple& t) std::string(j.type_name()))); } - from_json_tuple_impl(std::forward(j), t, index_sequence_for {}); + return from_json_tuple_impl(std::forward(j), std::forward(t), idx, priority_tag<1> {}); } -template -Tuple from_json_tuple_impl(BasicJsonType&& j, tag /*unused*/, index_sequence /*unused*/) +template +auto from_json(BasicJsonType&& j, std::tuple& t) +-> decltype(from_json_tuple(std::forward(j), t, index_sequence_for {})) { - return std::make_tuple(std::forward(j).at(Idx).template get::type>()...); + from_json_tuple(std::forward(j), t, index_sequence_for {}); } -template < typename BasicJsonType, typename... Args, - enable_if_t < !std::is_default_constructible>::value, int > = 0 > -std::tuple from_json(BasicJsonType && j, tag> t) +template +auto from_json(BasicJsonType&& j, identity_tag> tag) +-> decltype(from_json_tuple(std::forward(j), std::move(tag), 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()))); - } - - return from_json_tuple_impl(std::forward(j), t, index_sequence_for {}); + return from_json_tuple(std::forward(j), std::move(tag), index_sequence_for {}); } template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator, @@ -455,21 +456,11 @@ void from_json(const BasicJsonType& j, std::unordered_map - auto operator()(const BasicJsonType& j, T& val) const - noexcept(noexcept(from_json(j, val))) - -> decltype(from_json(j, val), void()) - { - return from_json(j, val); - } - - // overload to pass calls to built-in from_json functions for non-default constructible STL - // types (e.g. std::array, where X is not default constructible). - template - auto operator()(const BasicJsonType& j, detail::tag t) const - noexcept(noexcept(from_json(j, t))) - -> decltype(from_json(j, t)) + auto operator()(const BasicJsonType& j, T&& val) const + noexcept(noexcept(from_json(j, std::forward(val)))) + -> decltype(from_json(j, std::forward(val))) { - return from_json(j, t); + return from_json(j, std::forward(val)); } }; } // namespace detail diff --git a/include/nlohmann/detail/meta/tag.hpp b/include/nlohmann/detail/meta/identity_tag.hpp similarity index 75% rename from include/nlohmann/detail/meta/tag.hpp rename to include/nlohmann/detail/meta/identity_tag.hpp index 631887d1d2..73a3e91700 100644 --- a/include/nlohmann/detail/meta/tag.hpp +++ b/include/nlohmann/detail/meta/identity_tag.hpp @@ -5,6 +5,6 @@ namespace nlohmann namespace detail { // dispatching helper struct -template struct tag {}; +template struct identity_tag {}; } // namespace detail } // namespace nlohmann diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 06f7caa378..f3c1b987c1 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -2813,7 +2813,7 @@ constexpr T static_const::value; } // namespace detail } // namespace nlohmann -// #include +// #include namespace nlohmann @@ -2821,7 +2821,7 @@ namespace nlohmann namespace detail { // dispatching helper struct -template struct tag {}; +template struct identity_tag {}; } // namespace detail } // namespace nlohmann @@ -3747,16 +3747,16 @@ void()) } template < typename BasicJsonType, typename Array, std::size_t... Is > -Array from_json_array_impl(BasicJsonType&& j, tag /*unused*/, index_sequence /*unused*/) +Array from_json_array_impl(BasicJsonType&& j, identity_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) +auto from_json(BasicJsonType && j, identity_tag> tag) -> decltype(j.template get(), -from_json_array_impl(std::forward(j), t, make_index_sequence {})) +from_json_array_impl(std::forward(j), tag, make_index_sequence {})) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { @@ -3764,7 +3764,7 @@ from_json_array_impl(std::forward(j), t, make_index_sequence { std::string(j.type_name()))); } - return from_json_array_impl(std::forward(j), t, make_index_sequence {}); + return from_json_array_impl(std::forward(j), tag, make_index_sequence {}); } template @@ -3842,24 +3842,24 @@ void from_json(const BasicJsonType& j, ArithmeticType& val) } } +template < typename BasicJsonType, class A1, class A2 > +std::pair from_json_pair_impl(BasicJsonType&& j, identity_tag> /*unused*/, priority_tag<0> /*unused*/) +{ + return {std::forward(j).at(0).template get(), + std::forward(j).at(1).template get()}; +} + + template>::value, int> = 0> -void from_json(BasicJsonType && j, std::pair& p) +void from_json_pair_impl(BasicJsonType && j, std::pair& p, priority_tag<1> /*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()))); - } - - p = {std::forward(j).at(0).template get(), - std::forward(j).at(1).template get() - }; + p = from_json_pair_impl(std::forward(j), identity_tag> {}, priority_tag<0> {}); } -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*/) +template +auto from_json(BasicJsonType&& j, PairRelatedType&& p) +-> decltype(from_json_pair_impl(std::forward(j), std::forward(p), priority_tag<1> {})) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { @@ -3867,19 +3867,25 @@ std::pair from_json(BasicJsonType && j, tag> /*unused* std::string(j.type_name()))); } - return {std::forward(j).at(0).template get(), - std::forward(j).at(1).template get()}; + return from_json_pair_impl(std::forward(j), std::forward(p), priority_tag<1> {}); } template -void from_json_tuple_impl(BasicJsonType&& j, Tuple& t, index_sequence /*unused*/) +Tuple from_json_tuple_impl(BasicJsonType&& j, identity_tag /*unused*/, index_sequence /*unused*/, priority_tag<0> /*unused*/) +{ + return std::make_tuple(std::forward(j).at(Idx).template get::type>()...); +} + +template::value, int> = 0> +void from_json_tuple_impl(BasicJsonType && j, Tuple& t, index_sequence /*unused*/, priority_tag<1> /*unused*/) { - t = std::make_tuple(std::forward(j).at(Idx).template get::type>()...); + t = from_json_tuple_impl(std::forward(j), identity_tag {}, priority_tag<0> {}); } -template>::value, int > = 0 > -void from_json(BasicJsonType && j, std::tuple& t) +template +auto from_json_tuple(BasicJsonType&& j, TupleRelated&& t, index_sequence idx) +-> decltype(from_json_tuple_impl(std::forward(j), std::forward(t), idx, priority_tag<1> {})) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { @@ -3887,26 +3893,21 @@ void from_json(BasicJsonType && j, std::tuple& t) std::string(j.type_name()))); } - from_json_tuple_impl(std::forward(j), t, index_sequence_for {}); + return from_json_tuple_impl(std::forward(j), std::forward(t), idx, priority_tag<1> {}); } -template -Tuple from_json_tuple_impl(BasicJsonType&& j, tag /*unused*/, index_sequence /*unused*/) +template +auto from_json(BasicJsonType&& j, std::tuple& t) +-> decltype(from_json_tuple(std::forward(j), t, index_sequence_for {})) { - return std::make_tuple(std::forward(j).at(Idx).template get::type>()...); + from_json_tuple(std::forward(j), t, index_sequence_for {}); } -template < typename BasicJsonType, typename... Args, - enable_if_t < !std::is_default_constructible>::value, int > = 0 > -std::tuple from_json(BasicJsonType && j, tag> t) +template +auto from_json(BasicJsonType&& j, identity_tag> tag) +-> decltype(from_json_tuple(std::forward(j), std::move(tag), 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()))); - } - - return from_json_tuple_impl(std::forward(j), t, index_sequence_for {}); + return from_json_tuple(std::forward(j), std::move(tag), index_sequence_for {}); } template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator, @@ -3952,21 +3953,11 @@ void from_json(const BasicJsonType& j, std::unordered_map - auto operator()(const BasicJsonType& j, T& val) const - noexcept(noexcept(from_json(j, val))) - -> decltype(from_json(j, val), void()) - { - return from_json(j, val); - } - - // overload to pass calls to built-in from_json functions for non-default constructible STL - // types (e.g. std::array, where X is not default constructible). - template - auto operator()(const BasicJsonType& j, detail::tag t) const - noexcept(noexcept(from_json(j, t))) - -> decltype(from_json(j, t)) + auto operator()(const BasicJsonType& j, T&& val) const + noexcept(noexcept(from_json(j, std::forward(val)))) + -> decltype(from_json(j, std::forward(val))) { - return from_json(j, t); + return from_json(j, std::forward(val)); } }; } // namespace detail @@ -4540,6 +4531,8 @@ constexpr const auto& to_json = detail::static_const::value; } // namespace } // namespace nlohmann +// #include + // #include @@ -4582,10 +4575,10 @@ struct adl_serializer */ 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 {})) + noexcept(::nlohmann::from_json(std::forward(j), detail::identity_tag {}))) + -> decltype(::nlohmann::from_json(std::forward(j), detail::identity_tag {})) { - return ::nlohmann::from_json(std::forward(j), detail::tag {}); + return ::nlohmann::from_json(std::forward(j), detail::identity_tag {}); } /*! From 672e8bfc1d788384432c612859f7cb080ace8803 Mon Sep 17 00:00:00 2001 From: Anthony VH Date: Mon, 11 Jan 2021 18:16:15 +0100 Subject: [PATCH 05/17] Fixed std::pair trying to deserialize via array functions. --- .../nlohmann/detail/conversions/from_json.hpp | 25 +++++++++++-------- single_include/nlohmann/json.hpp | 25 +++++++++++-------- test/src/unit-regression2.cpp | 3 --- 3 files changed, 30 insertions(+), 23 deletions(-) diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index b29ac05c64..aaf75e2187 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -249,17 +249,23 @@ 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, identity_tag /*unused*/, index_sequence /*unused*/) +template < typename T, typename BasicJsonType, typename ArrayType, std::size_t... Idx> +ArrayType from_json_inplace_array_impl_base(BasicJsonType&& j, identity_tag /*unused*/, + index_sequence /*unused*/) { - return { std::forward(j).at(Is).template get()... }; + return { std::forward(j).at(Idx).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, identity_tag> tag) --> decltype(j.template get(), -from_json_array_impl(std::forward(j), tag, make_index_sequence {})) +template < typename BasicJsonType, typename T, std::size_t N > +auto from_json_inplace_array_impl(BasicJsonType&& j, identity_tag> tag, priority_tag<0> /*unused*/) +-> decltype(from_json_inplace_array_impl_base(std::forward(j), tag, make_index_sequence {})) +{ + return from_json_inplace_array_impl_base(std::forward(j), tag, make_index_sequence {}); +} + +template < typename BasicJsonType, typename ArrayType > +auto from_json(BasicJsonType&& j, identity_tag tag) +-> decltype(from_json_inplace_array_impl(std::forward(j), tag, priority_tag<0> {})) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { @@ -267,7 +273,7 @@ from_json_array_impl(std::forward(j), tag, make_index_sequence std::string(j.type_name()))); } - return from_json_array_impl(std::forward(j), tag, make_index_sequence {}); + return from_json_inplace_array_impl(std::forward(j), tag, priority_tag<0> {}); } template @@ -352,7 +358,6 @@ std::pair from_json_pair_impl(BasicJsonType&& j, identity_tag(j).at(1).template get()}; } - template>::value, int> = 0> void from_json_pair_impl(BasicJsonType && j, std::pair& p, priority_tag<1> /*unused*/) diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index f3c1b987c1..b452ca713f 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -3746,17 +3746,23 @@ 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, identity_tag /*unused*/, index_sequence /*unused*/) +template < typename T, typename BasicJsonType, typename ArrayType, std::size_t... Idx> +ArrayType from_json_inplace_array_impl_base(BasicJsonType&& j, identity_tag /*unused*/, + index_sequence /*unused*/) { - return { std::forward(j).at(Is).template get()... }; + return { std::forward(j).at(Idx).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, identity_tag> tag) --> decltype(j.template get(), -from_json_array_impl(std::forward(j), tag, make_index_sequence {})) +template < typename BasicJsonType, typename T, std::size_t N > +auto from_json_inplace_array_impl(BasicJsonType&& j, identity_tag> tag, priority_tag<0> /*unused*/) +-> decltype(from_json_inplace_array_impl_base(std::forward(j), tag, make_index_sequence {})) +{ + return from_json_inplace_array_impl_base(std::forward(j), tag, make_index_sequence {}); +} + +template < typename BasicJsonType, typename ArrayType > +auto from_json(BasicJsonType&& j, identity_tag tag) +-> decltype(from_json_inplace_array_impl(std::forward(j), tag, priority_tag<0> {})) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { @@ -3764,7 +3770,7 @@ from_json_array_impl(std::forward(j), tag, make_index_sequence std::string(j.type_name()))); } - return from_json_array_impl(std::forward(j), tag, make_index_sequence {}); + return from_json_inplace_array_impl(std::forward(j), tag, priority_tag<0> {}); } template @@ -3849,7 +3855,6 @@ std::pair from_json_pair_impl(BasicJsonType&& j, identity_tag(j).at(1).template get()}; } - template>::value, int> = 0> void from_json_pair_impl(BasicJsonType && j, std::pair& p, priority_tag<1> /*unused*/) diff --git a/test/src/unit-regression2.cpp b/test/src/unit-regression2.cpp index 1046bb2e58..e31296fac6 100644 --- a/test/src/unit-regression2.cpp +++ b/test/src/unit-regression2.cpp @@ -543,9 +543,6 @@ TEST_CASE("regression tests 2") { { 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); From 6ebf274ca093939af7bb5e921a20221b9204d410 Mon Sep 17 00:00:00 2001 From: Anthony VH Date: Tue, 12 Jan 2021 18:28:00 +0100 Subject: [PATCH 06/17] Add internal version of is_default_constructible to work around LWG 2367. --- include/nlohmann/detail/meta/type_traits.hpp | 73 +++++++++++++++----- 1 file changed, 56 insertions(+), 17 deletions(-) diff --git a/include/nlohmann/detail/meta/type_traits.hpp b/include/nlohmann/detail/meta/type_traits.hpp index e30d99e4ce..1fbf7cde7b 100644 --- a/include/nlohmann/detail/meta/type_traits.hpp +++ b/include/nlohmann/detail/meta/type_traits.hpp @@ -149,6 +149,52 @@ struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> /////////////////// // is_ functions // /////////////////// +// https://en.cppreference.com/w/cpp/types/conjunction +template struct conjunction : std::true_type { }; +template struct conjunction : B1 { }; +template +struct conjunction +: std::conditional, B1>::type {}; + +// Reimplementation of is_constructible and is_default_constructible, due to them being broken for +// std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367). +// This causes compile errors in e.g. clang 3.5 or gcc 4.9. +// Based on commit fixing this in gcc: https://github.com/gcc-mirror/gcc/commit/d3c64041b32b6962ad6b2d879231537a477631fb +template +struct is_default_constructible : std::is_default_constructible {}; + +template +struct is_default_constructible> + : conjunction, is_default_constructible> {}; + +template +struct is_default_constructible> + : conjunction, is_default_constructible> {}; + +template +struct is_default_constructible> + : conjunction...> {}; + +template +struct is_default_constructible> + : conjunction...> {}; + + +template +struct is_constructible : std::is_constructible {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + template struct is_iterator_traits : std::false_type {}; @@ -192,9 +238,9 @@ struct is_compatible_object_type_impl < // macOS's is_constructible does not play well with nonesuch... static constexpr bool value = - std::is_constructible::value && - std::is_constructible::value; }; @@ -215,10 +261,10 @@ struct is_constructible_object_type_impl < using object_t = typename BasicJsonType::object_t; static constexpr bool value = - (std::is_default_constructible::value && + (is_default_constructible::value && (std::is_move_assignable::value || std::is_copy_assignable::value) && - (std::is_constructible::value && std::is_same < typename object_t::mapped_type, @@ -246,7 +292,7 @@ struct is_compatible_string_type_impl < value_type_t, CompatibleStringType>::value >> { static constexpr auto value = - std::is_constructible::value; + is_constructible::value; }; template @@ -264,7 +310,7 @@ struct is_constructible_string_type_impl < value_type_t, ConstructibleStringType>::value >> { static constexpr auto value = - std::is_constructible::value; }; @@ -287,7 +333,7 @@ struct is_compatible_array_type_impl < iterator_traits>::value >> { static constexpr bool value = - std::is_constructible::value; }; @@ -310,7 +356,7 @@ struct is_constructible_array_type_impl < BasicJsonType, ConstructibleArrayType, enable_if_t < !std::is_same::value&& - std::is_default_constructible::value&& + is_default_constructible::value&& (std::is_move_assignable::value || std::is_copy_assignable::value)&& is_detected::value&& @@ -354,7 +400,7 @@ struct is_compatible_integer_type_impl < using CompatibleLimits = std::numeric_limits; static constexpr auto value = - std::is_constructible::value && CompatibleLimits::is_integer && RealLimits::is_signed == CompatibleLimits::is_signed; @@ -381,17 +427,10 @@ template struct is_compatible_type : is_compatible_type_impl {}; -// https://en.cppreference.com/w/cpp/types/conjunction -template struct conjunction : std::true_type { }; -template struct conjunction : B1 { }; -template -struct conjunction -: std::conditional, B1>::type {}; - template struct is_constructible_tuple : std::false_type {}; template -struct is_constructible_tuple> : conjunction...> {}; +struct is_constructible_tuple> : conjunction...> {}; } // namespace detail } // namespace nlohmann From 6278f31d23d77f472618b4cf3b0f9a37fc1e742a Mon Sep 17 00:00:00 2001 From: Anthony VH Date: Tue, 12 Jan 2021 18:28:29 +0100 Subject: [PATCH 07/17] Simplify from_json overloads. --- .../nlohmann/detail/conversions/from_json.hpp | 65 +++++++------------ 1 file changed, 24 insertions(+), 41 deletions(-) diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index aaf75e2187..fcb6a5e60e 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -249,23 +249,16 @@ void()) from_json_array_impl(j, arr, priority_tag<3> {}); } -template < typename T, typename BasicJsonType, typename ArrayType, std::size_t... Idx> -ArrayType from_json_inplace_array_impl_base(BasicJsonType&& j, identity_tag /*unused*/, - index_sequence /*unused*/) +template < typename BasicJsonType, typename T, std::size_t... Idx > +std::array from_json_inplace_array_impl(BasicJsonType&& j, + identity_tag> /*unused*/, index_sequence /*unused*/) { return { std::forward(j).at(Idx).template get()... }; } template < typename BasicJsonType, typename T, std::size_t N > -auto from_json_inplace_array_impl(BasicJsonType&& j, identity_tag> tag, priority_tag<0> /*unused*/) --> decltype(from_json_inplace_array_impl_base(std::forward(j), tag, make_index_sequence {})) -{ - return from_json_inplace_array_impl_base(std::forward(j), tag, make_index_sequence {}); -} - -template < typename BasicJsonType, typename ArrayType > -auto from_json(BasicJsonType&& j, identity_tag tag) --> decltype(from_json_inplace_array_impl(std::forward(j), tag, priority_tag<0> {})) +auto from_json(BasicJsonType&& j, identity_tag> tag) +-> decltype(from_json_inplace_array_impl(std::forward(j), tag, make_index_sequence {})) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { @@ -273,7 +266,7 @@ auto from_json(BasicJsonType&& j, identity_tag tag) std::string(j.type_name()))); } - return from_json_inplace_array_impl(std::forward(j), tag, priority_tag<0> {}); + return from_json_inplace_array_impl(std::forward(j), tag, make_index_sequence {}); } template @@ -358,9 +351,8 @@ std::pair from_json_pair_impl(BasicJsonType&& j, identity_tag(j).at(1).template get()}; } -template>::value, int> = 0> -void from_json_pair_impl(BasicJsonType && j, std::pair& p, priority_tag<1> /*unused*/) +template +void from_json_pair_impl(BasicJsonType&& j, std::pair& p, priority_tag<1> /*unused*/) { p = from_json_pair_impl(std::forward(j), identity_tag> {}, priority_tag<0> {}); } @@ -378,22 +370,27 @@ auto from_json(BasicJsonType&& j, PairRelatedType&& p) return from_json_pair_impl(std::forward(j), std::forward(p), priority_tag<1> {}); } -template -Tuple from_json_tuple_impl(BasicJsonType&& j, identity_tag /*unused*/, index_sequence /*unused*/, priority_tag<0> /*unused*/) +template +std::tuple from_json_tuple_impl_base(BasicJsonType&& j, index_sequence /*unused*/) +{ + return std::make_tuple(std::forward(j).at(Idx).template get()...); +} + +template +std::tuple from_json_tuple_impl(BasicJsonType&& j, identity_tag> /*unused*/, priority_tag<0> /*unused*/) { - return std::make_tuple(std::forward(j).at(Idx).template get::type>()...); + return from_json_tuple_impl_base(std::forward(j), index_sequence_for {}); } -template::value, int> = 0> -void from_json_tuple_impl(BasicJsonType && j, Tuple& t, index_sequence /*unused*/, priority_tag<1> /*unused*/) +template +void from_json_tuple_impl(BasicJsonType&& j, std::tuple& t, priority_tag<1> /*unused*/) { - t = from_json_tuple_impl(std::forward(j), identity_tag {}, priority_tag<0> {}); + t = from_json_tuple_impl_base(std::forward(j), index_sequence_for {}); } -template -auto from_json_tuple(BasicJsonType&& j, TupleRelated&& t, index_sequence idx) --> decltype(from_json_tuple_impl(std::forward(j), std::forward(t), idx, priority_tag<1> {})) +template +auto from_json(BasicJsonType&& j, TupleRelated&& t) +-> decltype(from_json_tuple_impl(std::forward(j), std::forward(t), priority_tag<1> {})) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { @@ -401,21 +398,7 @@ auto from_json_tuple(BasicJsonType&& j, TupleRelated&& t, index_sequence std::string(j.type_name()))); } - return from_json_tuple_impl(std::forward(j), std::forward(t), idx, priority_tag<1> {}); -} - -template -auto from_json(BasicJsonType&& j, std::tuple& t) --> decltype(from_json_tuple(std::forward(j), t, index_sequence_for {})) -{ - from_json_tuple(std::forward(j), t, index_sequence_for {}); -} - -template -auto from_json(BasicJsonType&& j, identity_tag> tag) --> decltype(from_json_tuple(std::forward(j), std::move(tag), index_sequence_for {})) -{ - return from_json_tuple(std::forward(j), std::move(tag), index_sequence_for {}); + return from_json_tuple_impl(std::forward(j), std::forward(t), priority_tag<1> {}); } template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator, From 6ef1614fa92879aec9b328e95ade3be2a97cc820 Mon Sep 17 00:00:00 2001 From: Anthony VH Date: Tue, 12 Jan 2021 18:28:47 +0100 Subject: [PATCH 08/17] Refactor json::get() to use priority_tag. --- include/nlohmann/json.hpp | 312 +++++++++++++++++++++----------------- 1 file changed, 169 insertions(+), 143 deletions(-) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 2ca029dd13..500acb36ec 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -2838,50 +2838,53 @@ class basic_json /// @{ /*! - @brief get special-case overload + @brief get a pointer value (implicit) - This overloads avoids a lot of template boilerplate, it can be seen as the - identity method + Implicit pointer access to the internally stored JSON value. No copies are + made. - @tparam BasicJsonType == @ref basic_json + @warning Writing data to the pointee of the result yields an undefined + state. - @return a copy of *this + @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, + @ref number_unsigned_t, or @ref number_float_t. Enforced by a static + assertion. + + @return pointer to the internally stored JSON value if the requested + pointer type @a PointerType fits to the JSON value; `nullptr` otherwise @complexity Constant. - @since version 2.1.0 + @liveexample{The example below shows how pointers to internal values of a + JSON value can be requested. Note that no type conversions are made and a + `nullptr` is returned if the value and the requested pointer type does not + match.,get_ptr} + + @since version 1.0.0 */ - template::type, basic_json_t>::value, - int> = 0> - basic_json get() const + template::value, int>::type = 0> + auto get_ptr() noexcept -> decltype(std::declval().get_impl_ptr(std::declval())) { - return *this; + // delegate the call to get_impl_ptr<>() + return get_impl_ptr(static_cast(nullptr)); } /*! - @brief get special-case overload - - This overloads converts the current @ref basic_json in a different - @ref basic_json type - - @tparam BasicJsonType == @ref basic_json - - @return a copy of *this, converted into @tparam BasicJsonType - - @complexity Depending on the implementation of the called `from_json()` - method. - - @since version 3.2.0 + @brief get a pointer value (implicit) + @copydoc get_ptr() */ - template < typename BasicJsonType, detail::enable_if_t < - !std::is_same::value&& - detail::is_basic_json::value, int > = 0 > - BasicJsonType get() const + template < typename PointerType, typename std::enable_if < + std::is_pointer::value&& + std::is_const::type>::value, int >::type = 0 > + constexpr auto get_ptr() const noexcept -> decltype(std::declval().get_impl_ptr(std::declval())) { - return *this; + // delegate the call to get_impl_ptr<>() const + return get_impl_ptr(static_cast(nullptr)); } + private: /*! @brief get a value (explicit) @@ -2923,21 +2926,12 @@ class basic_json */ template < typename ValueTypeCV, typename ValueType = detail::uncvref_t, detail::enable_if_t < - !detail::is_basic_json::value && - detail::has_from_json::value && - !detail::has_non_default_from_json::value, + detail::is_default_constructible::value && + detail::has_from_json::value, int > = 0 > - ValueType get() const noexcept(noexcept( - JSONSerializer::from_json(std::declval(), std::declval()))) + ValueType get_impl(detail::priority_tag<0> /*unused*/) const noexcept(noexcept( + JSONSerializer::from_json(std::declval(), std::declval()))) { - // we cannot static_assert on ValueTypeCV being non-const, because - // there is support for get(), which is why we - // still need the uncvref - static_assert(!std::is_reference::value, - "get() cannot be used with reference types, you might want to use get_ref()"); - static_assert(std::is_default_constructible::value, - "types must be DefaultConstructible when used with get()"); - ValueType ret; JSONSerializer::from_json(*this, ret); return ret; @@ -2975,133 +2969,107 @@ class basic_json @since version 2.1.0 */ template < typename ValueTypeCV, typename ValueType = detail::uncvref_t, - detail::enable_if_t < !std::is_same::value && - detail::has_non_default_from_json::value, - int > = 0 > - ValueType get() const noexcept(noexcept( - JSONSerializer::from_json(std::declval()))) + detail::enable_if_t < + detail::has_non_default_from_json::value, + int > = 0 > + ValueType get_impl(detail::priority_tag<1> /*unused*/) const noexcept(noexcept( + JSONSerializer::from_json(std::declval()))) { - static_assert(!std::is_reference::value, - "get() cannot be used with reference types, you might want to use get_ref()"); return JSONSerializer::from_json(*this); } /*! - @brief get a value (explicit) - - Explicit type conversion between the JSON value and a compatible value. - The value is filled into the input parameter by calling the @ref json_serializer - `from_json()` method. - - The function is equivalent to executing - @code {.cpp} - ValueType v; - JSONSerializer::from_json(*this, v); - @endcode - - This overloads is chosen if: - - @a ValueType is not @ref basic_json, - - @ref json_serializer has a `from_json()` method of the form - `void from_json(const basic_json&, ValueType&)`, and + @brief get special-case overload - @tparam ValueType the input parameter type. + This overloads converts the current @ref basic_json in a different + @ref basic_json type - @return the input parameter, allowing chaining calls. + @tparam BasicJsonType == @ref basic_json - @throw what @ref json_serializer `from_json()` method throws + @return a copy of *this, converted into @tparam BasicJsonType - @liveexample{The example below shows several conversions from JSON values - to other types. There a few things to note: (1) Floating-point numbers can - be converted to integers\, (2) A JSON array can be converted to a standard - `std::vector`\, (3) A JSON object can be converted to C++ - associative containers such as `std::unordered_map`.,get_to} + @complexity Depending on the implementation of the called `from_json()` + method. - @since version 3.3.0 + @since version 3.2.0 */ - template < typename ValueType, - detail::enable_if_t < - !detail::is_basic_json::value&& - detail::has_from_json::value, + template < typename BasicJsonType, detail::enable_if_t < + detail::is_basic_json::value, int > = 0 > - ValueType & get_to(ValueType& v) const noexcept(noexcept( - JSONSerializer::from_json(std::declval(), v))) + BasicJsonType get_impl(detail::priority_tag<2> /*unused*/) const { - JSONSerializer::from_json(*this, v); - return v; + return *this; } - // specialization to allow to call get_to with a basic_json value - // see https://github.com/nlohmann/json/issues/2175 - template::value, + /*! + @brief get special-case overload + + This overloads avoids a lot of template boilerplate, it can be seen as the + identity method + + @tparam BasicJsonType == @ref basic_json + + @return a copy of *this + + @complexity Constant. + + @since version 2.1.0 + */ + template::value, int> = 0> - ValueType & get_to(ValueType& v) const + basic_json get_impl(detail::priority_tag<3> /*unused*/) const { - v = *this; - return v; + return *this; } - template < - typename T, std::size_t N, - typename Array = T (&)[N], - detail::enable_if_t < - detail::has_from_json::value, int > = 0 > - Array get_to(T (&v)[N]) const - noexcept(noexcept(JSONSerializer::from_json( - std::declval(), v))) + /*! + @brief get a pointer value (explicit) + @copydoc get() + */ + template::value, int> = 0> + constexpr auto get_impl(detail::priority_tag<4> /*unused*/) const noexcept + -> decltype(std::declval().template get_ptr()) { - JSONSerializer::from_json(*this, v); - return v; + // delegate the call to get_ptr + return get_ptr(); } - + public: /*! - @brief get a pointer value (implicit) + @brief get a (pointer) value (explicit) - Implicit pointer access to the internally stored JSON value. No copies are - made. + Performs explicit type conversion between the JSON value and a compatible value if required. - @warning Writing data to the pointee of the result yields an undefined - state. + - If the requested type is a pointer to the internally stored JSON value that pointer is returned. + No copies are made. - @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref - object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, - @ref number_unsigned_t, or @ref number_float_t. Enforced by a static - assertion. + - If the requested type is the current @ref basic_json, or a different @ref basic_json convertible + from the current @ref basic_json. - @return pointer to the internally stored JSON value if the requested - pointer type @a PointerType fits to the JSON value; `nullptr` otherwise + - Otherwise the value is converted by calling the @ref json_serializer `from_json()` + method. - @complexity Constant. + @tparam ValueTypeCV the provided value type + @tparam ValueType the returned value type - @liveexample{The example below shows how pointers to internal values of a - JSON value can be requested. Note that no type conversions are made and a - `nullptr` is returned if the value and the requested pointer type does not - match.,get_ptr} + @return copy of the JSON value, converted to @tparam ValueType if necessary - @since version 1.0.0 - */ - template::value, int>::type = 0> - auto get_ptr() noexcept -> decltype(std::declval().get_impl_ptr(std::declval())) - { - // delegate the call to get_impl_ptr<>() - return get_impl_ptr(static_cast(nullptr)); - } + @throw what @ref json_serializer `from_json()` method throws if conversion is required - /*! - @brief get a pointer value (implicit) - @copydoc get_ptr() + @since version 2.1.0 */ - template < typename PointerType, typename std::enable_if < - std::is_pointer::value&& - std::is_const::type>::value, int >::type = 0 > - constexpr auto get_ptr() const noexcept -> decltype(std::declval().get_impl_ptr(std::declval())) + template < typename ValueTypeCV, typename ValueType = detail::uncvref_t> + constexpr auto get() const noexcept(noexcept(get_impl(detail::priority_tag<4> {}))) + -> decltype(get_impl(detail::priority_tag<4> {})) { - // delegate the call to get_impl_ptr<>() const - return get_impl_ptr(static_cast(nullptr)); + // we cannot static_assert on ValueTypeCV being non-const, because + // there is support for get(), which is why we + // still need the uncvref + static_assert(!std::is_reference::value, + "get() cannot be used with reference types, you might want to use get_ref()"); + return get_impl(detail::priority_tag<4> {}); } /*! @@ -3140,15 +3108,73 @@ class basic_json } /*! - @brief get a pointer value (explicit) - @copydoc get() + @brief get a value (explicit) + + Explicit type conversion between the JSON value and a compatible value. + The value is filled into the input parameter by calling the @ref json_serializer + `from_json()` method. + + The function is equivalent to executing + @code {.cpp} + ValueType v; + JSONSerializer::from_json(*this, v); + @endcode + + This overloads is chosen if: + - @a ValueType is not @ref basic_json, + - @ref json_serializer has a `from_json()` method of the form + `void from_json(const basic_json&, ValueType&)`, and + + @tparam ValueType the input parameter type. + + @return the input parameter, allowing chaining calls. + + @throw what @ref json_serializer `from_json()` method throws + + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,get_to} + + @since version 3.3.0 */ - template::value, int>::type = 0> - constexpr auto get() const noexcept -> decltype(std::declval().template get_ptr()) + template < typename ValueType, + detail::enable_if_t < + !detail::is_basic_json::value&& + detail::has_from_json::value, + int > = 0 > + ValueType & get_to(ValueType& v) const noexcept(noexcept( + JSONSerializer::from_json(std::declval(), v))) { - // delegate the call to get_ptr - return get_ptr(); + JSONSerializer::from_json(*this, v); + return v; + } + + // specialization to allow to call get_to with a basic_json value + // see https://github.com/nlohmann/json/issues/2175 + template::value, + int> = 0> + ValueType & get_to(ValueType& v) const + { + v = *this; + return v; + } + + template < + typename T, std::size_t N, + typename Array = T (&)[N], + detail::enable_if_t < + detail::has_from_json::value, int > = 0 > + Array get_to(T (&v)[N]) const + noexcept(noexcept(JSONSerializer::from_json( + std::declval(), v))) + { + JSONSerializer::from_json(*this, v); + return v; } /*! From fc8c584288162e73a9745ba18eacfc48252a1535 Mon Sep 17 00:00:00 2001 From: Anthony VH Date: Tue, 12 Jan 2021 18:29:05 +0100 Subject: [PATCH 09/17] Regenerated single include. --- single_include/nlohmann/json.hpp | 450 +++++++++++++++++-------------- 1 file changed, 249 insertions(+), 201 deletions(-) diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index b452ca713f..004850c3aa 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -3185,6 +3185,52 @@ struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> /////////////////// // is_ functions // /////////////////// +// https://en.cppreference.com/w/cpp/types/conjunction +template struct conjunction : std::true_type { }; +template struct conjunction : B1 { }; +template +struct conjunction +: std::conditional, B1>::type {}; + +// Reimplementation of is_constructible and is_default_constructible, due to them being broken for +// std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367). +// This causes compile errors in e.g. clang 3.5 or gcc 4.9. +// Based on commit fixing this in gcc: https://github.com/gcc-mirror/gcc/commit/d3c64041b32b6962ad6b2d879231537a477631fb +template +struct is_default_constructible : std::is_default_constructible {}; + +template +struct is_default_constructible> + : conjunction, is_default_constructible> {}; + +template +struct is_default_constructible> + : conjunction, is_default_constructible> {}; + +template +struct is_default_constructible> + : conjunction...> {}; + +template +struct is_default_constructible> + : conjunction...> {}; + + +template +struct is_constructible : std::is_constructible {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + template struct is_iterator_traits : std::false_type {}; @@ -3228,9 +3274,9 @@ struct is_compatible_object_type_impl < // macOS's is_constructible does not play well with nonesuch... static constexpr bool value = - std::is_constructible::value && - std::is_constructible::value; }; @@ -3251,10 +3297,10 @@ struct is_constructible_object_type_impl < using object_t = typename BasicJsonType::object_t; static constexpr bool value = - (std::is_default_constructible::value && + (is_default_constructible::value && (std::is_move_assignable::value || std::is_copy_assignable::value) && - (std::is_constructible::value && std::is_same < typename object_t::mapped_type, @@ -3282,7 +3328,7 @@ struct is_compatible_string_type_impl < value_type_t, CompatibleStringType>::value >> { static constexpr auto value = - std::is_constructible::value; + is_constructible::value; }; template @@ -3300,7 +3346,7 @@ struct is_constructible_string_type_impl < value_type_t, ConstructibleStringType>::value >> { static constexpr auto value = - std::is_constructible::value; }; @@ -3323,7 +3369,7 @@ struct is_compatible_array_type_impl < iterator_traits>::value >> { static constexpr bool value = - std::is_constructible::value; }; @@ -3346,7 +3392,7 @@ struct is_constructible_array_type_impl < BasicJsonType, ConstructibleArrayType, enable_if_t < !std::is_same::value&& - std::is_default_constructible::value&& + is_default_constructible::value&& (std::is_move_assignable::value || std::is_copy_assignable::value)&& is_detected::value&& @@ -3390,7 +3436,7 @@ struct is_compatible_integer_type_impl < using CompatibleLimits = std::numeric_limits; static constexpr auto value = - std::is_constructible::value && CompatibleLimits::is_integer && RealLimits::is_signed == CompatibleLimits::is_signed; @@ -3417,18 +3463,11 @@ template struct is_compatible_type : is_compatible_type_impl {}; -// https://en.cppreference.com/w/cpp/types/conjunction -template struct conjunction : std::true_type { }; -template struct conjunction : B1 { }; -template -struct conjunction -: std::conditional, B1>::type {}; - template struct is_constructible_tuple : std::false_type {}; template -struct is_constructible_tuple> : conjunction...> {}; +struct is_constructible_tuple> : conjunction...> {}; } // namespace detail } // namespace nlohmann @@ -3746,23 +3785,16 @@ void()) from_json_array_impl(j, arr, priority_tag<3> {}); } -template < typename T, typename BasicJsonType, typename ArrayType, std::size_t... Idx> -ArrayType from_json_inplace_array_impl_base(BasicJsonType&& j, identity_tag /*unused*/, - index_sequence /*unused*/) +template < typename BasicJsonType, typename T, std::size_t... Idx > +std::array from_json_inplace_array_impl(BasicJsonType&& j, + identity_tag> /*unused*/, index_sequence /*unused*/) { return { std::forward(j).at(Idx).template get()... }; } template < typename BasicJsonType, typename T, std::size_t N > -auto from_json_inplace_array_impl(BasicJsonType&& j, identity_tag> tag, priority_tag<0> /*unused*/) --> decltype(from_json_inplace_array_impl_base(std::forward(j), tag, make_index_sequence {})) -{ - return from_json_inplace_array_impl_base(std::forward(j), tag, make_index_sequence {}); -} - -template < typename BasicJsonType, typename ArrayType > -auto from_json(BasicJsonType&& j, identity_tag tag) --> decltype(from_json_inplace_array_impl(std::forward(j), tag, priority_tag<0> {})) +auto from_json(BasicJsonType&& j, identity_tag> tag) +-> decltype(from_json_inplace_array_impl(std::forward(j), tag, make_index_sequence {})) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { @@ -3770,7 +3802,7 @@ auto from_json(BasicJsonType&& j, identity_tag tag) std::string(j.type_name()))); } - return from_json_inplace_array_impl(std::forward(j), tag, priority_tag<0> {}); + return from_json_inplace_array_impl(std::forward(j), tag, make_index_sequence {}); } template @@ -3855,9 +3887,8 @@ std::pair from_json_pair_impl(BasicJsonType&& j, identity_tag(j).at(1).template get()}; } -template>::value, int> = 0> -void from_json_pair_impl(BasicJsonType && j, std::pair& p, priority_tag<1> /*unused*/) +template +void from_json_pair_impl(BasicJsonType&& j, std::pair& p, priority_tag<1> /*unused*/) { p = from_json_pair_impl(std::forward(j), identity_tag> {}, priority_tag<0> {}); } @@ -3875,22 +3906,27 @@ auto from_json(BasicJsonType&& j, PairRelatedType&& p) return from_json_pair_impl(std::forward(j), std::forward(p), priority_tag<1> {}); } -template -Tuple from_json_tuple_impl(BasicJsonType&& j, identity_tag /*unused*/, index_sequence /*unused*/, priority_tag<0> /*unused*/) +template +std::tuple from_json_tuple_impl_base(BasicJsonType&& j, index_sequence /*unused*/) { - return std::make_tuple(std::forward(j).at(Idx).template get::type>()...); + return std::make_tuple(std::forward(j).at(Idx).template get()...); } -template::value, int> = 0> -void from_json_tuple_impl(BasicJsonType && j, Tuple& t, index_sequence /*unused*/, priority_tag<1> /*unused*/) +template +std::tuple from_json_tuple_impl(BasicJsonType&& j, identity_tag> /*unused*/, priority_tag<0> /*unused*/) { - t = from_json_tuple_impl(std::forward(j), identity_tag {}, priority_tag<0> {}); + return from_json_tuple_impl_base(std::forward(j), index_sequence_for {}); } -template -auto from_json_tuple(BasicJsonType&& j, TupleRelated&& t, index_sequence idx) --> decltype(from_json_tuple_impl(std::forward(j), std::forward(t), idx, priority_tag<1> {})) +template +void from_json_tuple_impl(BasicJsonType&& j, std::tuple& t, priority_tag<1> /*unused*/) +{ + t = from_json_tuple_impl_base(std::forward(j), index_sequence_for {}); +} + +template +auto from_json(BasicJsonType&& j, TupleRelated&& t) +-> decltype(from_json_tuple_impl(std::forward(j), std::forward(t), priority_tag<1> {})) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { @@ -3898,21 +3934,7 @@ auto from_json_tuple(BasicJsonType&& j, TupleRelated&& t, index_sequence std::string(j.type_name()))); } - return from_json_tuple_impl(std::forward(j), std::forward(t), idx, priority_tag<1> {}); -} - -template -auto from_json(BasicJsonType&& j, std::tuple& t) --> decltype(from_json_tuple(std::forward(j), t, index_sequence_for {})) -{ - from_json_tuple(std::forward(j), t, index_sequence_for {}); -} - -template -auto from_json(BasicJsonType&& j, identity_tag> tag) --> decltype(from_json_tuple(std::forward(j), std::move(tag), index_sequence_for {})) -{ - return from_json_tuple(std::forward(j), std::move(tag), index_sequence_for {}); + return from_json_tuple_impl(std::forward(j), std::forward(t), priority_tag<1> {}); } template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator, @@ -19575,50 +19597,53 @@ class basic_json /// @{ /*! - @brief get special-case overload + @brief get a pointer value (implicit) - This overloads avoids a lot of template boilerplate, it can be seen as the - identity method + Implicit pointer access to the internally stored JSON value. No copies are + made. - @tparam BasicJsonType == @ref basic_json + @warning Writing data to the pointee of the result yields an undefined + state. - @return a copy of *this + @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, + @ref number_unsigned_t, or @ref number_float_t. Enforced by a static + assertion. + + @return pointer to the internally stored JSON value if the requested + pointer type @a PointerType fits to the JSON value; `nullptr` otherwise @complexity Constant. - @since version 2.1.0 + @liveexample{The example below shows how pointers to internal values of a + JSON value can be requested. Note that no type conversions are made and a + `nullptr` is returned if the value and the requested pointer type does not + match.,get_ptr} + + @since version 1.0.0 */ - template::type, basic_json_t>::value, - int> = 0> - basic_json get() const + template::value, int>::type = 0> + auto get_ptr() noexcept -> decltype(std::declval().get_impl_ptr(std::declval())) { - return *this; + // delegate the call to get_impl_ptr<>() + return get_impl_ptr(static_cast(nullptr)); } /*! - @brief get special-case overload - - This overloads converts the current @ref basic_json in a different - @ref basic_json type - - @tparam BasicJsonType == @ref basic_json - - @return a copy of *this, converted into @tparam BasicJsonType - - @complexity Depending on the implementation of the called `from_json()` - method. - - @since version 3.2.0 + @brief get a pointer value (implicit) + @copydoc get_ptr() */ - template < typename BasicJsonType, detail::enable_if_t < - !std::is_same::value&& - detail::is_basic_json::value, int > = 0 > - BasicJsonType get() const + template < typename PointerType, typename std::enable_if < + std::is_pointer::value&& + std::is_const::type>::value, int >::type = 0 > + constexpr auto get_ptr() const noexcept -> decltype(std::declval().get_impl_ptr(std::declval())) { - return *this; + // delegate the call to get_impl_ptr<>() const + return get_impl_ptr(static_cast(nullptr)); } + private: /*! @brief get a value (explicit) @@ -19660,21 +19685,12 @@ class basic_json */ template < typename ValueTypeCV, typename ValueType = detail::uncvref_t, detail::enable_if_t < - !detail::is_basic_json::value && - detail::has_from_json::value && - !detail::has_non_default_from_json::value, + detail::is_default_constructible::value && + detail::has_from_json::value, int > = 0 > - ValueType get() const noexcept(noexcept( - JSONSerializer::from_json(std::declval(), std::declval()))) + ValueType get_impl(detail::priority_tag<0> /*unused*/) const noexcept(noexcept( + JSONSerializer::from_json(std::declval(), std::declval()))) { - // we cannot static_assert on ValueTypeCV being non-const, because - // there is support for get(), which is why we - // still need the uncvref - static_assert(!std::is_reference::value, - "get() cannot be used with reference types, you might want to use get_ref()"); - static_assert(std::is_default_constructible::value, - "types must be DefaultConstructible when used with get()"); - ValueType ret; JSONSerializer::from_json(*this, ret); return ret; @@ -19712,133 +19728,107 @@ class basic_json @since version 2.1.0 */ template < typename ValueTypeCV, typename ValueType = detail::uncvref_t, - detail::enable_if_t < !std::is_same::value && - detail::has_non_default_from_json::value, - int > = 0 > - ValueType get() const noexcept(noexcept( - JSONSerializer::from_json(std::declval()))) + detail::enable_if_t < + detail::has_non_default_from_json::value, + int > = 0 > + ValueType get_impl(detail::priority_tag<1> /*unused*/) const noexcept(noexcept( + JSONSerializer::from_json(std::declval()))) { - static_assert(!std::is_reference::value, - "get() cannot be used with reference types, you might want to use get_ref()"); return JSONSerializer::from_json(*this); } /*! - @brief get a value (explicit) - - Explicit type conversion between the JSON value and a compatible value. - The value is filled into the input parameter by calling the @ref json_serializer - `from_json()` method. - - The function is equivalent to executing - @code {.cpp} - ValueType v; - JSONSerializer::from_json(*this, v); - @endcode - - This overloads is chosen if: - - @a ValueType is not @ref basic_json, - - @ref json_serializer has a `from_json()` method of the form - `void from_json(const basic_json&, ValueType&)`, and + @brief get special-case overload - @tparam ValueType the input parameter type. + This overloads converts the current @ref basic_json in a different + @ref basic_json type - @return the input parameter, allowing chaining calls. + @tparam BasicJsonType == @ref basic_json - @throw what @ref json_serializer `from_json()` method throws + @return a copy of *this, converted into @tparam BasicJsonType - @liveexample{The example below shows several conversions from JSON values - to other types. There a few things to note: (1) Floating-point numbers can - be converted to integers\, (2) A JSON array can be converted to a standard - `std::vector`\, (3) A JSON object can be converted to C++ - associative containers such as `std::unordered_map`.,get_to} + @complexity Depending on the implementation of the called `from_json()` + method. - @since version 3.3.0 + @since version 3.2.0 */ - template < typename ValueType, - detail::enable_if_t < - !detail::is_basic_json::value&& - detail::has_from_json::value, + template < typename BasicJsonType, detail::enable_if_t < + detail::is_basic_json::value, int > = 0 > - ValueType & get_to(ValueType& v) const noexcept(noexcept( - JSONSerializer::from_json(std::declval(), v))) + BasicJsonType get_impl(detail::priority_tag<2> /*unused*/) const { - JSONSerializer::from_json(*this, v); - return v; + return *this; } - // specialization to allow to call get_to with a basic_json value - // see https://github.com/nlohmann/json/issues/2175 - template::value, + /*! + @brief get special-case overload + + This overloads avoids a lot of template boilerplate, it can be seen as the + identity method + + @tparam BasicJsonType == @ref basic_json + + @return a copy of *this + + @complexity Constant. + + @since version 2.1.0 + */ + template::value, int> = 0> - ValueType & get_to(ValueType& v) const + basic_json get_impl(detail::priority_tag<3> /*unused*/) const { - v = *this; - return v; + return *this; } - template < - typename T, std::size_t N, - typename Array = T (&)[N], - detail::enable_if_t < - detail::has_from_json::value, int > = 0 > - Array get_to(T (&v)[N]) const - noexcept(noexcept(JSONSerializer::from_json( - std::declval(), v))) + /*! + @brief get a pointer value (explicit) + @copydoc get() + */ + template::value, int> = 0> + constexpr auto get_impl(detail::priority_tag<4> /*unused*/) const noexcept + -> decltype(std::declval().template get_ptr()) { - JSONSerializer::from_json(*this, v); - return v; + // delegate the call to get_ptr + return get_ptr(); } - + public: /*! - @brief get a pointer value (implicit) + @brief get a (pointer) value (explicit) - Implicit pointer access to the internally stored JSON value. No copies are - made. + Performs explicit type conversion between the JSON value and a compatible value if required. - @warning Writing data to the pointee of the result yields an undefined - state. + - If the requested type is a pointer to the internally stored JSON value that pointer is returned. + No copies are made. - @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref - object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, - @ref number_unsigned_t, or @ref number_float_t. Enforced by a static - assertion. + - If the requested type is the current @ref basic_json, or a different @ref basic_json convertible + from the current @ref basic_json. - @return pointer to the internally stored JSON value if the requested - pointer type @a PointerType fits to the JSON value; `nullptr` otherwise + - Otherwise the value is converted by calling the @ref json_serializer `from_json()` + method. - @complexity Constant. + @tparam ValueTypeCV the provided value type + @tparam ValueType the returned value type - @liveexample{The example below shows how pointers to internal values of a - JSON value can be requested. Note that no type conversions are made and a - `nullptr` is returned if the value and the requested pointer type does not - match.,get_ptr} + @return copy of the JSON value, converted to @tparam ValueType if necessary - @since version 1.0.0 - */ - template::value, int>::type = 0> - auto get_ptr() noexcept -> decltype(std::declval().get_impl_ptr(std::declval())) - { - // delegate the call to get_impl_ptr<>() - return get_impl_ptr(static_cast(nullptr)); - } + @throw what @ref json_serializer `from_json()` method throws if conversion is required - /*! - @brief get a pointer value (implicit) - @copydoc get_ptr() + @since version 2.1.0 */ - template < typename PointerType, typename std::enable_if < - std::is_pointer::value&& - std::is_const::type>::value, int >::type = 0 > - constexpr auto get_ptr() const noexcept -> decltype(std::declval().get_impl_ptr(std::declval())) + template < typename ValueTypeCV, typename ValueType = detail::uncvref_t> + constexpr auto get() const noexcept(noexcept(get_impl(detail::priority_tag<4> {}))) + -> decltype(get_impl(detail::priority_tag<4> {})) { - // delegate the call to get_impl_ptr<>() const - return get_impl_ptr(static_cast(nullptr)); + // we cannot static_assert on ValueTypeCV being non-const, because + // there is support for get(), which is why we + // still need the uncvref + static_assert(!std::is_reference::value, + "get() cannot be used with reference types, you might want to use get_ref()"); + return get_impl(detail::priority_tag<4> {}); } /*! @@ -19877,15 +19867,73 @@ class basic_json } /*! - @brief get a pointer value (explicit) - @copydoc get() + @brief get a value (explicit) + + Explicit type conversion between the JSON value and a compatible value. + The value is filled into the input parameter by calling the @ref json_serializer + `from_json()` method. + + The function is equivalent to executing + @code {.cpp} + ValueType v; + JSONSerializer::from_json(*this, v); + @endcode + + This overloads is chosen if: + - @a ValueType is not @ref basic_json, + - @ref json_serializer has a `from_json()` method of the form + `void from_json(const basic_json&, ValueType&)`, and + + @tparam ValueType the input parameter type. + + @return the input parameter, allowing chaining calls. + + @throw what @ref json_serializer `from_json()` method throws + + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,get_to} + + @since version 3.3.0 */ - template::value, int>::type = 0> - constexpr auto get() const noexcept -> decltype(std::declval().template get_ptr()) + template < typename ValueType, + detail::enable_if_t < + !detail::is_basic_json::value&& + detail::has_from_json::value, + int > = 0 > + ValueType & get_to(ValueType& v) const noexcept(noexcept( + JSONSerializer::from_json(std::declval(), v))) { - // delegate the call to get_ptr - return get_ptr(); + JSONSerializer::from_json(*this, v); + return v; + } + + // specialization to allow to call get_to with a basic_json value + // see https://github.com/nlohmann/json/issues/2175 + template::value, + int> = 0> + ValueType & get_to(ValueType& v) const + { + v = *this; + return v; + } + + template < + typename T, std::size_t N, + typename Array = T (&)[N], + detail::enable_if_t < + detail::has_from_json::value, int > = 0 > + Array get_to(T (&v)[N]) const + noexcept(noexcept(JSONSerializer::from_json( + std::declval(), v))) + { + JSONSerializer::from_json(*this, v); + return v; } /*! From fbf6df63d9e16171abdcf47b6d699a1b19aaf1f9 Mon Sep 17 00:00:00 2001 From: Anthony VH Date: Wed, 13 Jan 2021 00:12:51 +0100 Subject: [PATCH 10/17] Enable member function calls in trailing return decltype expressions for older compilers. --- include/nlohmann/json.hpp | 27 +++++++++++++++------------ single_include/nlohmann/json.hpp | 27 +++++++++++++++------------ 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 500acb36ec..a7adb724fd 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -2908,7 +2908,6 @@ class basic_json - @ref json_serializer does not have a `from_json()` method of the form `ValueType from_json(const basic_json&)` - @tparam ValueTypeCV the provided value type @tparam ValueType the returned value type @return copy of the JSON value, converted to @a ValueType @@ -2924,9 +2923,9 @@ class basic_json @since version 2.1.0 */ - template < typename ValueTypeCV, typename ValueType = detail::uncvref_t, + template < typename ValueType, detail::enable_if_t < - detail::is_default_constructible::value && + detail::is_default_constructible::value&& detail::has_from_json::value, int > = 0 > ValueType get_impl(detail::priority_tag<0> /*unused*/) const noexcept(noexcept( @@ -2948,7 +2947,7 @@ class basic_json The function is equivalent to executing @code {.cpp} - return JSONSerializer::from_json(*this); + return JSONSerializer::from_json(*this); @endcode This overloads is chosen if: @@ -2959,7 +2958,6 @@ class basic_json @note If @ref json_serializer has both overloads of `from_json()`, this one is chosen. - @tparam ValueTypeCV the provided value type @tparam ValueType the returned value type @return copy of the JSON value, converted to @a ValueType @@ -2968,7 +2966,7 @@ class basic_json @since version 2.1.0 */ - template < typename ValueTypeCV, typename ValueType = detail::uncvref_t, + template < typename ValueType, detail::enable_if_t < detail::has_non_default_from_json::value, int > = 0 > @@ -2993,7 +2991,8 @@ class basic_json @since version 3.2.0 */ - template < typename BasicJsonType, detail::enable_if_t < + template < typename BasicJsonType, + detail::enable_if_t < detail::is_basic_json::value, int > = 0 > BasicJsonType get_impl(detail::priority_tag<2> /*unused*/) const @@ -3015,7 +3014,8 @@ class basic_json @since version 2.1.0 */ - template::value, int> = 0> basic_json get_impl(detail::priority_tag<3> /*unused*/) const @@ -3027,8 +3027,10 @@ class basic_json @brief get a pointer value (explicit) @copydoc get() */ - template::value, int> = 0> + template::value, + int> = 0> constexpr auto get_impl(detail::priority_tag<4> /*unused*/) const noexcept -> decltype(std::declval().template get_ptr()) { @@ -3061,8 +3063,9 @@ class basic_json @since version 2.1.0 */ template < typename ValueTypeCV, typename ValueType = detail::uncvref_t> - constexpr auto get() const noexcept(noexcept(get_impl(detail::priority_tag<4> {}))) - -> decltype(get_impl(detail::priority_tag<4> {})) + constexpr auto get() const noexcept( + noexcept(std::declval().template get_impl(detail::priority_tag<4> {}))) + -> decltype(std::declval().template get_impl(detail::priority_tag<4> {})) { // we cannot static_assert on ValueTypeCV being non-const, because // there is support for get(), which is why we diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 004850c3aa..fd01826b4f 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -19667,7 +19667,6 @@ class basic_json - @ref json_serializer does not have a `from_json()` method of the form `ValueType from_json(const basic_json&)` - @tparam ValueTypeCV the provided value type @tparam ValueType the returned value type @return copy of the JSON value, converted to @a ValueType @@ -19683,9 +19682,9 @@ class basic_json @since version 2.1.0 */ - template < typename ValueTypeCV, typename ValueType = detail::uncvref_t, + template < typename ValueType, detail::enable_if_t < - detail::is_default_constructible::value && + detail::is_default_constructible::value&& detail::has_from_json::value, int > = 0 > ValueType get_impl(detail::priority_tag<0> /*unused*/) const noexcept(noexcept( @@ -19707,7 +19706,7 @@ class basic_json The function is equivalent to executing @code {.cpp} - return JSONSerializer::from_json(*this); + return JSONSerializer::from_json(*this); @endcode This overloads is chosen if: @@ -19718,7 +19717,6 @@ class basic_json @note If @ref json_serializer has both overloads of `from_json()`, this one is chosen. - @tparam ValueTypeCV the provided value type @tparam ValueType the returned value type @return copy of the JSON value, converted to @a ValueType @@ -19727,7 +19725,7 @@ class basic_json @since version 2.1.0 */ - template < typename ValueTypeCV, typename ValueType = detail::uncvref_t, + template < typename ValueType, detail::enable_if_t < detail::has_non_default_from_json::value, int > = 0 > @@ -19752,7 +19750,8 @@ class basic_json @since version 3.2.0 */ - template < typename BasicJsonType, detail::enable_if_t < + template < typename BasicJsonType, + detail::enable_if_t < detail::is_basic_json::value, int > = 0 > BasicJsonType get_impl(detail::priority_tag<2> /*unused*/) const @@ -19774,7 +19773,8 @@ class basic_json @since version 2.1.0 */ - template::value, int> = 0> basic_json get_impl(detail::priority_tag<3> /*unused*/) const @@ -19786,8 +19786,10 @@ class basic_json @brief get a pointer value (explicit) @copydoc get() */ - template::value, int> = 0> + template::value, + int> = 0> constexpr auto get_impl(detail::priority_tag<4> /*unused*/) const noexcept -> decltype(std::declval().template get_ptr()) { @@ -19820,8 +19822,9 @@ class basic_json @since version 2.1.0 */ template < typename ValueTypeCV, typename ValueType = detail::uncvref_t> - constexpr auto get() const noexcept(noexcept(get_impl(detail::priority_tag<4> {}))) - -> decltype(get_impl(detail::priority_tag<4> {})) + constexpr auto get() const noexcept( + noexcept(std::declval().template get_impl(detail::priority_tag<4> {}))) + -> decltype(std::declval().template get_impl(detail::priority_tag<4> {})) { // we cannot static_assert on ValueTypeCV being non-const, because // there is support for get(), which is why we From d7c0f157c5bab277fb65c2412cb9cf75e0f920a0 Mon Sep 17 00:00:00 2001 From: Anthony VH Date: Wed, 13 Jan 2021 20:47:03 +0100 Subject: [PATCH 11/17] Merged from_json for pair and tuple to try to fix C2995 error in old MSVC versions. --- .../nlohmann/detail/conversions/from_json.hpp | 49 ++++++++----------- single_include/nlohmann/json.hpp | 49 ++++++++----------- 2 files changed, 42 insertions(+), 56 deletions(-) diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index fcb6a5e60e..9523943cab 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -188,7 +188,10 @@ auto from_json_array_impl(const BasicJsonType& j, std::array& arr, } } -template +template::value, + int> = 0> auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/) -> decltype( arr.reserve(std::declval()), @@ -209,7 +212,10 @@ auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, p arr = std::move(ret); } -template +template::value, + int> = 0> void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<0> /*unused*/) { @@ -344,53 +350,40 @@ void from_json(const BasicJsonType& j, ArithmeticType& val) } } +template +std::tuple from_json_tuple_impl_base(BasicJsonType&& j, index_sequence /*unused*/) +{ + return std::make_tuple(std::forward(j).at(Idx).template get()...); +} + template < typename BasicJsonType, class A1, class A2 > -std::pair from_json_pair_impl(BasicJsonType&& j, identity_tag> /*unused*/, priority_tag<0> /*unused*/) +std::pair from_json_tuple_impl(BasicJsonType&& j, identity_tag> /*unused*/, priority_tag<0> /*unused*/) { return {std::forward(j).at(0).template get(), std::forward(j).at(1).template get()}; } template -void from_json_pair_impl(BasicJsonType&& j, std::pair& p, priority_tag<1> /*unused*/) +void from_json_tuple_impl(BasicJsonType&& j, std::pair& p, priority_tag<1> /*unused*/) { - p = from_json_pair_impl(std::forward(j), identity_tag> {}, priority_tag<0> {}); -} - -template -auto from_json(BasicJsonType&& j, PairRelatedType&& p) --> decltype(from_json_pair_impl(std::forward(j), std::forward(p), priority_tag<1> {})) -{ - 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_pair_impl(std::forward(j), std::forward(p), priority_tag<1> {}); -} - -template -std::tuple from_json_tuple_impl_base(BasicJsonType&& j, index_sequence /*unused*/) -{ - return std::make_tuple(std::forward(j).at(Idx).template get()...); + p = from_json_tuple_impl(std::forward(j), identity_tag> {}, priority_tag<0> {}); } template -std::tuple from_json_tuple_impl(BasicJsonType&& j, identity_tag> /*unused*/, priority_tag<0> /*unused*/) +std::tuple from_json_tuple_impl(BasicJsonType&& j, identity_tag> /*unused*/, priority_tag<2> /*unused*/) { return from_json_tuple_impl_base(std::forward(j), index_sequence_for {}); } template -void from_json_tuple_impl(BasicJsonType&& j, std::tuple& t, priority_tag<1> /*unused*/) +void from_json_tuple_impl(BasicJsonType&& j, std::tuple& t, priority_tag<3> /*unused*/) { t = from_json_tuple_impl_base(std::forward(j), index_sequence_for {}); } template auto from_json(BasicJsonType&& j, TupleRelated&& t) --> decltype(from_json_tuple_impl(std::forward(j), std::forward(t), priority_tag<1> {})) +-> decltype(from_json_tuple_impl(std::forward(j), std::forward(t), priority_tag<3> {})) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { @@ -398,7 +391,7 @@ auto from_json(BasicJsonType&& j, TupleRelated&& t) std::string(j.type_name()))); } - return from_json_tuple_impl(std::forward(j), std::forward(t), priority_tag<1> {}); + return from_json_tuple_impl(std::forward(j), std::forward(t), priority_tag<3> {}); } template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator, diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index fd01826b4f..f7ee9030b8 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -3724,7 +3724,10 @@ auto from_json_array_impl(const BasicJsonType& j, std::array& arr, } } -template +template::value, + int> = 0> auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/) -> decltype( arr.reserve(std::declval()), @@ -3745,7 +3748,10 @@ auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, p arr = std::move(ret); } -template +template::value, + int> = 0> void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<0> /*unused*/) { @@ -3880,53 +3886,40 @@ void from_json(const BasicJsonType& j, ArithmeticType& val) } } +template +std::tuple from_json_tuple_impl_base(BasicJsonType&& j, index_sequence /*unused*/) +{ + return std::make_tuple(std::forward(j).at(Idx).template get()...); +} + template < typename BasicJsonType, class A1, class A2 > -std::pair from_json_pair_impl(BasicJsonType&& j, identity_tag> /*unused*/, priority_tag<0> /*unused*/) +std::pair from_json_tuple_impl(BasicJsonType&& j, identity_tag> /*unused*/, priority_tag<0> /*unused*/) { return {std::forward(j).at(0).template get(), std::forward(j).at(1).template get()}; } template -void from_json_pair_impl(BasicJsonType&& j, std::pair& p, priority_tag<1> /*unused*/) -{ - p = from_json_pair_impl(std::forward(j), identity_tag> {}, priority_tag<0> {}); -} - -template -auto from_json(BasicJsonType&& j, PairRelatedType&& p) --> decltype(from_json_pair_impl(std::forward(j), std::forward(p), priority_tag<1> {})) +void from_json_tuple_impl(BasicJsonType&& j, std::pair& p, priority_tag<1> /*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 from_json_pair_impl(std::forward(j), std::forward(p), priority_tag<1> {}); -} - -template -std::tuple from_json_tuple_impl_base(BasicJsonType&& j, index_sequence /*unused*/) -{ - return std::make_tuple(std::forward(j).at(Idx).template get()...); + p = from_json_tuple_impl(std::forward(j), identity_tag> {}, priority_tag<0> {}); } template -std::tuple from_json_tuple_impl(BasicJsonType&& j, identity_tag> /*unused*/, priority_tag<0> /*unused*/) +std::tuple from_json_tuple_impl(BasicJsonType&& j, identity_tag> /*unused*/, priority_tag<2> /*unused*/) { return from_json_tuple_impl_base(std::forward(j), index_sequence_for {}); } template -void from_json_tuple_impl(BasicJsonType&& j, std::tuple& t, priority_tag<1> /*unused*/) +void from_json_tuple_impl(BasicJsonType&& j, std::tuple& t, priority_tag<3> /*unused*/) { t = from_json_tuple_impl_base(std::forward(j), index_sequence_for {}); } template auto from_json(BasicJsonType&& j, TupleRelated&& t) --> decltype(from_json_tuple_impl(std::forward(j), std::forward(t), priority_tag<1> {})) +-> decltype(from_json_tuple_impl(std::forward(j), std::forward(t), priority_tag<3> {})) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { @@ -3934,7 +3927,7 @@ auto from_json(BasicJsonType&& j, TupleRelated&& t) std::string(j.type_name()))); } - return from_json_tuple_impl(std::forward(j), std::forward(t), priority_tag<1> {}); + return from_json_tuple_impl(std::forward(j), std::forward(t), priority_tag<3> {}); } template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator, From 6eb37e9b7881057b6f6fb646a46fd2c1ea4c79a1 Mon Sep 17 00:00:00 2001 From: Anthony VH Date: Thu, 14 Jan 2021 12:39:03 +0100 Subject: [PATCH 12/17] Only add conditional constexpr to get() for >= C++14 to work around errors on older compilers. --- include/nlohmann/json.hpp | 5 ++++- single_include/nlohmann/json.hpp | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index a7adb724fd..e5a7917c5e 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -3063,7 +3063,10 @@ class basic_json @since version 2.1.0 */ template < typename ValueTypeCV, typename ValueType = detail::uncvref_t> - constexpr auto get() const noexcept( +#if defined(JSON_HAS_CPP_14) + constexpr +#endif + auto get() const noexcept( noexcept(std::declval().template get_impl(detail::priority_tag<4> {}))) -> decltype(std::declval().template get_impl(detail::priority_tag<4> {})) { diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index f7ee9030b8..7ce3138295 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -19815,7 +19815,10 @@ class basic_json @since version 2.1.0 */ template < typename ValueTypeCV, typename ValueType = detail::uncvref_t> - constexpr auto get() const noexcept( +#if defined(JSON_HAS_CPP_14) + constexpr +#endif + auto get() const noexcept( noexcept(std::declval().template get_impl(detail::priority_tag<4> {}))) -> decltype(std::declval().template get_impl(detail::priority_tag<4> {})) { From 848927ae90376d6e1e383bbd40073cf1aa09190a Mon Sep 17 00:00:00 2001 From: Anthony VH Date: Sat, 23 Jan 2021 18:24:47 +0100 Subject: [PATCH 13/17] Updated comments as requested. --- include/nlohmann/adl_serializer.hpp | 4 ++-- include/nlohmann/detail/meta/type_traits.hpp | 1 + single_include/nlohmann/json.hpp | 5 +++-- test/src/unit-regression2.cpp | 1 + 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/include/nlohmann/adl_serializer.hpp b/include/nlohmann/adl_serializer.hpp index 1dee29eb94..f967612db2 100644 --- a/include/nlohmann/adl_serializer.hpp +++ b/include/nlohmann/adl_serializer.hpp @@ -20,7 +20,7 @@ struct adl_serializer This function is usually called by the `get()` function of the @ref basic_json class (either explicit or via conversion operators). - @note This function is chosen for value types which can be default constructed. + @note This function is chosen for default-constructible value types. @param[in] j JSON value to read from @param[in,out] val value to write to @@ -39,7 +39,7 @@ struct adl_serializer This function is usually called by the `get()` function of the @ref basic_json class (either explicit or via conversion operators). - @note This function is chosen for value types which can not be default constructed. + @note This function is chosen for value types which are not default-constructible. @param[in] j JSON value to read from diff --git a/include/nlohmann/detail/meta/type_traits.hpp b/include/nlohmann/detail/meta/type_traits.hpp index 1fbf7cde7b..631829d07d 100644 --- a/include/nlohmann/detail/meta/type_traits.hpp +++ b/include/nlohmann/detail/meta/type_traits.hpp @@ -149,6 +149,7 @@ struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> /////////////////// // is_ functions // /////////////////// + // https://en.cppreference.com/w/cpp/types/conjunction template struct conjunction : std::true_type { }; template struct conjunction : B1 { }; diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 7ce3138295..79c822d552 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -3185,6 +3185,7 @@ struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> /////////////////// // is_ functions // /////////////////// + // https://en.cppreference.com/w/cpp/types/conjunction template struct conjunction : std::true_type { }; template struct conjunction : B1 { }; @@ -4568,7 +4569,7 @@ struct adl_serializer This function is usually called by the `get()` function of the @ref basic_json class (either explicit or via conversion operators). - @note This function is chosen for value types which can be default constructed. + @note This function is chosen for default-constructible value types. @param[in] j JSON value to read from @param[in,out] val value to write to @@ -4587,7 +4588,7 @@ struct adl_serializer This function is usually called by the `get()` function of the @ref basic_json class (either explicit or via conversion operators). - @note This function is chosen for value types which can not be default constructed. + @note This function is chosen for value types which are not default-constructible. @param[in] j JSON value to read from diff --git a/test/src/unit-regression2.cpp b/test/src/unit-regression2.cpp index e31296fac6..5a3de09d2b 100644 --- a/test/src/unit-regression2.cpp +++ b/test/src/unit-regression2.cpp @@ -136,6 +136,7 @@ struct NotSerializableData ///////////////////////////////////////////////////////////////////// // for #2574 ///////////////////////////////////////////////////////////////////// + struct NonDefaultConstructible { explicit NonDefaultConstructible (int x) : x(x) { } From 130382f2a90428d8fdd72e7b156d428764552efb Mon Sep 17 00:00:00 2001 From: Anthony VH Date: Sun, 24 Jan 2021 20:02:24 +0100 Subject: [PATCH 14/17] Remove comment about GCC commit which didn't really relate to the code. --- include/nlohmann/detail/meta/type_traits.hpp | 1 - single_include/nlohmann/json.hpp | 1 - 2 files changed, 2 deletions(-) diff --git a/include/nlohmann/detail/meta/type_traits.hpp b/include/nlohmann/detail/meta/type_traits.hpp index 631829d07d..22d0bfe044 100644 --- a/include/nlohmann/detail/meta/type_traits.hpp +++ b/include/nlohmann/detail/meta/type_traits.hpp @@ -160,7 +160,6 @@ struct conjunction // Reimplementation of is_constructible and is_default_constructible, due to them being broken for // std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367). // This causes compile errors in e.g. clang 3.5 or gcc 4.9. -// Based on commit fixing this in gcc: https://github.com/gcc-mirror/gcc/commit/d3c64041b32b6962ad6b2d879231537a477631fb template struct is_default_constructible : std::is_default_constructible {}; diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 79c822d552..7abc0f9b71 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -3196,7 +3196,6 @@ struct conjunction // Reimplementation of is_constructible and is_default_constructible, due to them being broken for // std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367). // This causes compile errors in e.g. clang 3.5 or gcc 4.9. -// Based on commit fixing this in gcc: https://github.com/gcc-mirror/gcc/commit/d3c64041b32b6962ad6b2d879231537a477631fb template struct is_default_constructible : std::is_default_constructible {}; From 322bc99d8ebede17b213029b43814643760b336f Mon Sep 17 00:00:00 2001 From: Anthony VH Date: Wed, 24 Mar 2021 13:03:36 +0100 Subject: [PATCH 15/17] Reran amalgamate. --- single_include/nlohmann/json.hpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 6ced652e14..62223ed28b 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -4000,8 +4000,7 @@ auto from_json(BasicJsonType&& j, identity_tag> tag) { if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + - std::string(j.type_name()))); + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); } return from_json_inplace_array_impl(std::forward(j), tag, make_index_sequence {}); @@ -4119,8 +4118,7 @@ auto from_json(BasicJsonType&& j, TupleRelated&& 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()))); + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); } return from_json_tuple_impl(std::forward(j), std::forward(t), priority_tag<3> {}); From 8e79917d3244d75a7bbe1b3586aa157eaf9f9198 Mon Sep 17 00:00:00 2001 From: Anthony VH Date: Wed, 24 Mar 2021 13:27:59 +0100 Subject: [PATCH 16/17] Fix clang-tidy complaints. --- test/src/unit-regression2.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/src/unit-regression2.cpp b/test/src/unit-regression2.cpp index 416ca16c59..f9873f5f4a 100644 --- a/test/src/unit-regression2.cpp +++ b/test/src/unit-regression2.cpp @@ -140,7 +140,7 @@ struct NotSerializableData struct NonDefaultConstructible { - explicit NonDefaultConstructible (int x) : x(x) { } + explicit NonDefaultConstructible (int a) : x(a) { } int x; }; @@ -154,7 +154,7 @@ struct adl_serializer return NonDefaultConstructible(j.get()); } }; -} +} // namespace nlohmann TEST_CASE("regression tests 2") From 2b865131d88dd37f5aa64944673fde9a344df19c Mon Sep 17 00:00:00 2001 From: Anthony VH Date: Wed, 24 Mar 2021 23:33:21 +0100 Subject: [PATCH 17/17] Fixing CI errors. --- include/nlohmann/detail/conversions/from_json.hpp | 2 +- single_include/nlohmann/json.hpp | 2 +- test/src/unit-regression2.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index c661dd66ee..4e4efd0a66 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -258,7 +258,7 @@ template < typename BasicJsonType, typename T, std::size_t... Idx > std::array from_json_inplace_array_impl(BasicJsonType&& j, identity_tag> /*unused*/, index_sequence /*unused*/) { - return { std::forward(j).at(Idx).template get()... }; + return { { std::forward(j).at(Idx).template get()... } }; } template < typename BasicJsonType, typename T, std::size_t N > diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 62223ed28b..6fde2fd8c5 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -3991,7 +3991,7 @@ template < typename BasicJsonType, typename T, std::size_t... Idx > std::array from_json_inplace_array_impl(BasicJsonType&& j, identity_tag> /*unused*/, index_sequence /*unused*/) { - return { std::forward(j).at(Idx).template get()... }; + return { { std::forward(j).at(Idx).template get()... } }; } template < typename BasicJsonType, typename T, std::size_t N > diff --git a/test/src/unit-regression2.cpp b/test/src/unit-regression2.cpp index f9873f5f4a..4d48e4765b 100644 --- a/test/src/unit-regression2.cpp +++ b/test/src/unit-regression2.cpp @@ -149,7 +149,7 @@ namespace nlohmann template <> struct adl_serializer { - static NonDefaultConstructible from_json (json const& j) noexcept + static NonDefaultConstructible from_json (json const& j) { return NonDefaultConstructible(j.get()); }