From 1e825e4f92b84bea316dbd935bfef0e1299b6ccf Mon Sep 17 00:00:00 2001 From: Anthony VH Date: Sat, 9 Jan 2021 00:08:27 +0100 Subject: [PATCH 01/32] 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 4af1c4bb1..8200c2809 100644 --- a/include/nlohmann/adl_serializer.hpp +++ b/include/nlohmann/adl_serializer.hpp @@ -1,14 +1,16 @@ #pragma once +#include #include #include #include +#include namespace nlohmann { -template +template struct adl_serializer { /*! @@ -20,14 +22,22 @@ struct adl_serializer @param[in] j JSON value to read from @param[in,out] val value to write to */ - template - static auto from_json(BasicJsonType&& j, ValueType& val) noexcept( + template + static auto from_json(BasicJsonType && j, U& val) noexcept( noexcept(::nlohmann::from_json(std::forward(j), val))) -> decltype(::nlohmann::from_json(std::forward(j), val), void()) { ::nlohmann::from_json(std::forward(j), val); } + template + static auto from_json(BasicJsonType && j) noexcept( + noexcept(::nlohmann::from_json(std::forward(j), detail::tag {}))) + -> decltype(::nlohmann::from_json(std::forward(j), detail::tag {})) + { + return ::nlohmann::from_json(std::forward(j), detail::tag {}); + } + /*! @brief convert any value type to a JSON value @@ -37,13 +47,12 @@ struct adl_serializer @param[in,out] j JSON value to write to @param[in] val value to read from */ - template - static auto to_json(BasicJsonType& j, ValueType&& val) noexcept( - noexcept(::nlohmann::to_json(j, std::forward(val)))) - -> decltype(::nlohmann::to_json(j, std::forward(val)), void()) + template + static auto to_json(BasicJsonType& j, U && val) noexcept( + noexcept(::nlohmann::to_json(j, std::forward(val)))) + -> decltype(::nlohmann::to_json(j, std::forward(val)), void()) { - ::nlohmann::to_json(j, std::forward(val)); + ::nlohmann::to_json(j, std::forward(val)); } }; - } // namespace nlohmann diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index 438b84a2e..557faa560 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -248,6 +249,27 @@ void()) from_json_array_impl(j, arr, priority_tag<3> {}); } +template < typename BasicJsonType, typename Array, std::size_t... Is > +Array from_json_array_impl(BasicJsonType&& j, tag /*unused*/, index_sequence /*unused*/) +{ + return { std::forward(j).at(Is).template get()... }; +} + +template < typename BasicJsonType, typename T, std::size_t N, + enable_if_t < !std::is_default_constructible>::value, int > = 0 > +auto from_json(BasicJsonType && j, tag> t) +-> decltype(j.template get(), +from_json_array_impl(std::forward(j), t, make_index_sequence {})) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + + std::string(j.type_name()))); + } + + return from_json_array_impl(std::forward(j), t, make_index_sequence {}); +} + template void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin) { @@ -323,22 +345,71 @@ void from_json(const BasicJsonType& j, ArithmeticType& val) } } -template -void from_json(const BasicJsonType& j, std::pair& p) +template>::value, int> = 0> +void from_json(BasicJsonType && j, std::pair& p) { - p = {j.at(0).template get(), j.at(1).template get()}; + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + + std::string(j.type_name()))); + } + + p = {std::forward(j).at(0).template get(), + std::forward(j).at(1).template get() + }; +} + +template < typename BasicJsonType, class A1, class A2, + enable_if_t < !std::is_default_constructible>::value, int > = 0 > +std::pair from_json(BasicJsonType && j, tag> /*unused*/) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + + std::string(j.type_name()))); + } + + return {std::forward(j).at(0).template get(), + std::forward(j).at(1).template get()}; } template -void from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence /*unused*/) +void from_json_tuple_impl(BasicJsonType&& j, Tuple& t, index_sequence /*unused*/) { - t = std::make_tuple(j.at(Idx).template get::type>()...); + t = std::make_tuple(std::forward(j).at(Idx).template get::type>()...); } -template -void from_json(const BasicJsonType& j, std::tuple& t) +template>::value, int > = 0 > +void from_json(BasicJsonType && j, std::tuple& t) { - from_json_tuple_impl(j, t, index_sequence_for {}); + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + + std::string(j.type_name()))); + } + + from_json_tuple_impl(std::forward(j), t, index_sequence_for {}); +} + +template +Tuple from_json_tuple_impl(BasicJsonType&& j, tag /*unused*/, index_sequence /*unused*/) +{ + return std::make_tuple(std::forward(j).at(Idx).template get::type>()...); +} + +template < typename BasicJsonType, typename... Args, + enable_if_t < !std::is_default_constructible>::value, int > = 0 > +std::tuple from_json(BasicJsonType && j, tag> t) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + + std::string(j.type_name()))); + } + + return from_json_tuple_impl(std::forward(j), t, index_sequence_for {}); } template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator, @@ -390,6 +461,14 @@ struct from_json_fn { return from_json(j, val); } + + template + auto operator()(const BasicJsonType& j, detail::tag t) const + noexcept(noexcept(from_json(j, t))) + -> decltype(from_json(j, t)) + { + return from_json(j, t); + } }; } // namespace detail diff --git a/include/nlohmann/detail/meta/tag.hpp b/include/nlohmann/detail/meta/tag.hpp new file mode 100644 index 000000000..631887d1d --- /dev/null +++ b/include/nlohmann/detail/meta/tag.hpp @@ -0,0 +1,10 @@ +#pragma once + +namespace nlohmann +{ +namespace detail +{ +// dispatching helper struct +template struct tag {}; +} // namespace detail +} // namespace nlohmann diff --git a/include/nlohmann/detail/meta/type_traits.hpp b/include/nlohmann/detail/meta/type_traits.hpp index 1706cbdc6..e30d99e4c 100644 --- a/include/nlohmann/detail/meta/type_traits.hpp +++ b/include/nlohmann/detail/meta/type_traits.hpp @@ -106,8 +106,7 @@ struct is_getable }; template -struct has_from_json < BasicJsonType, T, - enable_if_t < !is_basic_json::value >> +struct has_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> { using serializer = typename BasicJsonType::template json_serializer; diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 492118a5f..fd2811503 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -49,6 +49,7 @@ SOFTWARE. // #include +#include #include // #include @@ -2812,6 +2813,18 @@ constexpr T static_const::value; } // namespace detail } // namespace nlohmann +// #include + + +namespace nlohmann +{ +namespace detail +{ +// dispatching helper struct +template struct tag {}; +} // namespace detail +} // namespace nlohmann + // #include @@ -3129,8 +3142,7 @@ struct is_getable }; template -struct has_from_json < BasicJsonType, T, - enable_if_t < !is_basic_json::value >> +struct has_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> { using serializer = typename BasicJsonType::template json_serializer; @@ -3734,6 +3746,27 @@ void()) from_json_array_impl(j, arr, priority_tag<3> {}); } +template < typename BasicJsonType, typename Array, std::size_t... Is > +Array from_json_array_impl(BasicJsonType&& j, tag /*unused*/, index_sequence /*unused*/) +{ + return { std::forward(j).at(Is).template get()... }; +} + +template < typename BasicJsonType, typename T, std::size_t N, + enable_if_t < !std::is_default_constructible>::value, int > = 0 > +auto from_json(BasicJsonType && j, tag> t) +-> decltype(j.template get(), +from_json_array_impl(std::forward(j), t, make_index_sequence {})) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + + std::string(j.type_name()))); + } + + return from_json_array_impl(std::forward(j), t, make_index_sequence {}); +} + template void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin) { @@ -3809,22 +3842,71 @@ void from_json(const BasicJsonType& j, ArithmeticType& val) } } -template -void from_json(const BasicJsonType& j, std::pair& p) +template>::value, int> = 0> +void from_json(BasicJsonType && j, std::pair& p) { - p = {j.at(0).template get(), j.at(1).template get()}; + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + + std::string(j.type_name()))); + } + + p = {std::forward(j).at(0).template get(), + std::forward(j).at(1).template get() + }; +} + +template < typename BasicJsonType, class A1, class A2, + enable_if_t < !std::is_default_constructible>::value, int > = 0 > +std::pair from_json(BasicJsonType && j, tag> /*unused*/) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + + std::string(j.type_name()))); + } + + return {std::forward(j).at(0).template get(), + std::forward(j).at(1).template get()}; } template -void from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence /*unused*/) +void from_json_tuple_impl(BasicJsonType&& j, Tuple& t, index_sequence /*unused*/) { - t = std::make_tuple(j.at(Idx).template get::type>()...); + t = std::make_tuple(std::forward(j).at(Idx).template get::type>()...); } -template -void from_json(const BasicJsonType& j, std::tuple& t) +template>::value, int > = 0 > +void from_json(BasicJsonType && j, std::tuple& t) { - from_json_tuple_impl(j, t, index_sequence_for {}); + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + + std::string(j.type_name()))); + } + + from_json_tuple_impl(std::forward(j), t, index_sequence_for {}); +} + +template +Tuple from_json_tuple_impl(BasicJsonType&& j, tag /*unused*/, index_sequence /*unused*/) +{ + return std::make_tuple(std::forward(j).at(Idx).template get::type>()...); +} + +template < typename BasicJsonType, typename... Args, + enable_if_t < !std::is_default_constructible>::value, int > = 0 > +std::tuple from_json(BasicJsonType && j, tag> t) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + + std::string(j.type_name()))); + } + + return from_json_tuple_impl(std::forward(j), t, index_sequence_for {}); } template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator, @@ -3876,6 +3958,14 @@ struct from_json_fn { return from_json(j, val); } + + template + auto operator()(const BasicJsonType& j, detail::tag t) const + noexcept(noexcept(from_json(j, t))) + -> decltype(from_json(j, t)) + { + return from_json(j, t); + } }; } // namespace detail @@ -4448,11 +4538,13 @@ constexpr const auto& to_json = detail::static_const::value; } // namespace } // namespace nlohmann +// #include + namespace nlohmann { -template +template struct adl_serializer { /*! @@ -4464,14 +4556,22 @@ struct adl_serializer @param[in] j JSON value to read from @param[in,out] val value to write to */ - template - static auto from_json(BasicJsonType&& j, ValueType& val) noexcept( + template + static auto from_json(BasicJsonType && j, U& val) noexcept( noexcept(::nlohmann::from_json(std::forward(j), val))) -> decltype(::nlohmann::from_json(std::forward(j), val), void()) { ::nlohmann::from_json(std::forward(j), val); } + template + static auto from_json(BasicJsonType && j) noexcept( + noexcept(::nlohmann::from_json(std::forward(j), detail::tag {}))) + -> decltype(::nlohmann::from_json(std::forward(j), detail::tag {})) + { + return ::nlohmann::from_json(std::forward(j), detail::tag {}); + } + /*! @brief convert any value type to a JSON value @@ -4481,15 +4581,14 @@ struct adl_serializer @param[in,out] j JSON value to write to @param[in] val value to read from */ - template - static auto to_json(BasicJsonType& j, ValueType&& val) noexcept( - noexcept(::nlohmann::to_json(j, std::forward(val)))) - -> decltype(::nlohmann::to_json(j, std::forward(val)), void()) + template + static auto to_json(BasicJsonType& j, U && val) noexcept( + noexcept(::nlohmann::to_json(j, std::forward(val)))) + -> decltype(::nlohmann::to_json(j, std::forward(val)), void()) { - ::nlohmann::to_json(j, std::forward(val)); + ::nlohmann::to_json(j, std::forward(val)); } }; - } // namespace nlohmann // #include diff --git a/test/src/unit-regression2.cpp b/test/src/unit-regression2.cpp index 1e8c4922a..e2adff862 100644 --- a/test/src/unit-regression2.cpp +++ b/test/src/unit-regression2.cpp @@ -115,7 +115,7 @@ namespace nlohmann template <> struct adl_serializer { - static NonDefaultFromJsonStruct from_json (json const&) noexcept + static NonDefaultFromJsonStruct from_json (const json&) noexcept { return {}; } @@ -133,6 +133,28 @@ struct NotSerializableData }; +///////////////////////////////////////////////////////////////////// +// for #2574 +///////////////////////////////////////////////////////////////////// +struct NonDefaultConstructible +{ + explicit NonDefaultConstructible (int x) : x(x) { } + int x; +}; + +namespace nlohmann +{ +template <> +struct adl_serializer +{ + static NonDefaultConstructible from_json (const json& j) noexcept + { + return NonDefaultConstructible(j.get()); + } +}; +} + + TEST_CASE("regression tests 2") { SECTION("issue #1001 - Fix memory leak during parser callback") @@ -498,4 +520,59 @@ TEST_CASE("regression tests 2") CHECK(j.dump() == "\"Hello, world!\""); } #endif + + SECTION("issue #2574 - Deserialization to std::array, std::pair, and std::tuple with non-default constructable types fails") + { + SECTION("std::array") + { + json j = { 7, 4 }; + auto arr = j.get>(); + CHECK(arr[0].x == 7); + CHECK(arr[1].x == 4); + } + + SECTION("std::pair") + { + { + json j = { 3, 8 }; + auto x = j.at(0).get(); + CHECK(x.x == 3); + + auto p = j.get>(); + CHECK(p.first.x == 3); + CHECK(p.second.x == 8); + } + + { + json j = { 4, 1 }; + auto p = j.get>(); + CHECK(p.first == 4); + CHECK(p.second.x == 1); + } + + { + json j = { 6, 7 }; + auto p = j.get>(); + CHECK(p.first.x == 6); + CHECK(p.second == 7); + } + } + + SECTION("std::tuple") + { + { + json j = { 9 }; + auto t = j.get>(); + CHECK(std::get<0>(t).x == 9); + } + + { + json j = { 9, 8, 7 }; + auto t = j.get>(); + CHECK(std::get<0>(t).x == 9); + CHECK(std::get<1>(t) == 8); + CHECK(std::get<2>(t).x == 7); + } + } + } } From c0a8b45bbb4c9d857f1672a6199c1e8659ea9cd2 Mon Sep 17 00:00:00 2001 From: Anthony VH Date: Sat, 9 Jan 2021 17:45:56 +0100 Subject: [PATCH 02/32] 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 8200c2809..9eb751b70 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 557faa560..c9b88ce5b 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 fd2811503..06f7caa37 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/32] 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 e2adff862..1046bb2e5 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/32] Reduced code duplication, renamed tag to identity_tag. --- include/nlohmann/adl_serializer.hpp | 7 +- .../nlohmann/detail/conversions/from_json.hpp | 101 ++++++++-------- .../detail/meta/{tag.hpp => identity_tag.hpp} | 2 +- single_include/nlohmann/json.hpp | 111 ++++++++---------- 4 files changed, 103 insertions(+), 118 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 9eb751b70..1dee29eb9 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 c9b88ce5b..b29ac05c6 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,44 +345,24 @@ void from_json(const BasicJsonType& j, ArithmeticType& val) } } -template>::value, int> = 0> -void from_json(BasicJsonType && j, std::pair& p) +template < typename BasicJsonType, class A1, class A2 > +std::pair from_json_pair_impl(BasicJsonType&& j, identity_tag> /*unused*/, priority_tag<0> /*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() - }; -} - -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*/) + +template>::value, int> = 0> +void from_json_pair_impl(BasicJsonType && j, std::pair& p, priority_tag<1> /*unused*/) { - t = std::make_tuple(std::forward(j).at(Idx).template get::type>()...); + p = from_json_pair_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(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())) { @@ -390,18 +370,25 @@ 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_pair_impl(std::forward(j), std::forward(p), priority_tag<1> {}); } template -Tuple from_json_tuple_impl(BasicJsonType&& j, tag /*unused*/, 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 < typename BasicJsonType, typename... Args, - enable_if_t < !std::is_default_constructible>::value, int > = 0 > -std::tuple from_json(BasicJsonType && j, tag> t) +template::value, int> = 0> +void from_json_tuple_impl(BasicJsonType && j, Tuple& t, index_sequence /*unused*/, priority_tag<1> /*unused*/) +{ + t = from_json_tuple_impl(std::forward(j), identity_tag {}, priority_tag<0> {}); +} + +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())) { @@ -409,7 +396,21 @@ std::tuple from_json(BasicJsonType && j, tag> t) std::string(j.type_name()))); } - return 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 +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 {}); } 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()) + 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, 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)) - { - 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 631887d1d..73a3e9170 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 06f7caa37..f3c1b987c 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,44 +3842,24 @@ void from_json(const BasicJsonType& j, ArithmeticType& val) } } -template>::value, int> = 0> -void from_json(BasicJsonType && j, std::pair& p) +template < typename BasicJsonType, class A1, class A2 > +std::pair from_json_pair_impl(BasicJsonType&& j, identity_tag> /*unused*/, priority_tag<0> /*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() - }; -} - -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*/) + +template>::value, int> = 0> +void from_json_pair_impl(BasicJsonType && j, std::pair& p, priority_tag<1> /*unused*/) { - t = std::make_tuple(std::forward(j).at(Idx).template get::type>()...); + p = from_json_pair_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(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())) { @@ -3887,18 +3867,25 @@ 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_pair_impl(std::forward(j), std::forward(p), priority_tag<1> {}); } template -Tuple from_json_tuple_impl(BasicJsonType&& j, tag /*unused*/, 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 < typename BasicJsonType, typename... Args, - enable_if_t < !std::is_default_constructible>::value, int > = 0 > -std::tuple from_json(BasicJsonType && j, tag> t) +template::value, int> = 0> +void from_json_tuple_impl(BasicJsonType && j, Tuple& t, index_sequence /*unused*/, priority_tag<1> /*unused*/) +{ + t = from_json_tuple_impl(std::forward(j), identity_tag {}, priority_tag<0> {}); +} + +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())) { @@ -3906,7 +3893,21 @@ std::tuple from_json(BasicJsonType && j, tag> t) std::string(j.type_name()))); } - return 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 +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 {}); } 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()) + 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, 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)) - { - 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/32] 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 b29ac05c6..aaf75e218 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 f3c1b987c..b452ca713 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 1046bb2e5..e31296fac 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/32] 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 e30d99e4c..1fbf7cde7 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/32] 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 aaf75e218..fcb6a5e60 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::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())) { @@ -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/32] 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 2ca029dd1..500acb36e 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,15 +2969,142 @@ 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()))) { + return JSONSerializer::from_json(*this); + } + + /*! + @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 + */ + template < typename BasicJsonType, detail::enable_if_t < + detail::is_basic_json::value, + int > = 0 > + BasicJsonType get_impl(detail::priority_tag<2> /*unused*/) const + { + return *this; + } + + /*! + @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> + basic_json get_impl(detail::priority_tag<3> /*unused*/) const + { + return *this; + } + + /*! + @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()) + { + // delegate the call to get_ptr + return get_ptr(); + } + + public: + /*! + @brief get a (pointer) value (explicit) + + Performs explicit type conversion between the JSON value and a compatible value if required. + + - If the requested type is a pointer to the internally stored JSON value that pointer is returned. + No copies are made. + + - If the requested type is the current @ref basic_json, or a different @ref basic_json convertible + from the current @ref basic_json. + + - Otherwise the value is converted by calling the @ref json_serializer `from_json()` + method. + + @tparam ValueTypeCV the provided value type + @tparam ValueType the returned value type + + @return copy of the JSON value, converted to @tparam ValueType if necessary + + @throw what @ref json_serializer `from_json()` method throws if conversion is required + + @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> {})) + { + // 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 JSONSerializer::from_json(*this); + return get_impl(detail::priority_tag<4> {}); + } + + /*! + @brief get a pointer value (explicit) + + Explicit pointer access to the internally stored JSON value. No copies are + made. + + @warning The pointer becomes invalid if the underlying JSON object + changes. + + @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. + + @return pointer to the internally stored JSON value if the requested + pointer type @a PointerType fits to the JSON value; `nullptr` otherwise + + @complexity Constant. + + @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__PointerType} + + @sa @ref get_ptr() for explicit pointer-member access + + @since version 1.0.0 + */ + template::value, int>::type = 0> + auto get() noexcept -> decltype(std::declval().template get_ptr()) + { + // delegate the call to get_ptr + return get_ptr(); } /*! @@ -3056,101 +3177,6 @@ class basic_json return v; } - - /*! - @brief get a pointer value (implicit) - - Implicit pointer access to the internally stored JSON value. No copies are - made. - - @warning Writing data to the pointee of the result yields an undefined - state. - - @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. - - @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::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)); - } - - /*! - @brief get a pointer value (implicit) - @copydoc get_ptr() - */ - 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())) - { - // delegate the call to get_impl_ptr<>() const - return get_impl_ptr(static_cast(nullptr)); - } - - /*! - @brief get a pointer value (explicit) - - Explicit pointer access to the internally stored JSON value. No copies are - made. - - @warning The pointer becomes invalid if the underlying JSON object - changes. - - @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. - - @return pointer to the internally stored JSON value if the requested - pointer type @a PointerType fits to the JSON value; `nullptr` otherwise - - @complexity Constant. - - @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__PointerType} - - @sa @ref get_ptr() for explicit pointer-member access - - @since version 1.0.0 - */ - template::value, int>::type = 0> - auto get() noexcept -> decltype(std::declval().template get_ptr()) - { - // delegate the call to get_ptr - return get_ptr(); - } - - /*! - @brief get a pointer value (explicit) - @copydoc get() - */ - template::value, int>::type = 0> - constexpr auto get() const noexcept -> decltype(std::declval().template get_ptr()) - { - // delegate the call to get_ptr - return get_ptr(); - } - /*! @brief get a reference value (implicit) From fc8c584288162e73a9745ba18eacfc48252a1535 Mon Sep 17 00:00:00 2001 From: Anthony VH Date: Tue, 12 Jan 2021 18:29:05 +0100 Subject: [PATCH 09/32] 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 b452ca713..004850c3a 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,15 +19728,142 @@ 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()))) { + return JSONSerializer::from_json(*this); + } + + /*! + @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 + */ + template < typename BasicJsonType, detail::enable_if_t < + detail::is_basic_json::value, + int > = 0 > + BasicJsonType get_impl(detail::priority_tag<2> /*unused*/) const + { + return *this; + } + + /*! + @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> + basic_json get_impl(detail::priority_tag<3> /*unused*/) const + { + return *this; + } + + /*! + @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()) + { + // delegate the call to get_ptr + return get_ptr(); + } + + public: + /*! + @brief get a (pointer) value (explicit) + + Performs explicit type conversion between the JSON value and a compatible value if required. + + - If the requested type is a pointer to the internally stored JSON value that pointer is returned. + No copies are made. + + - If the requested type is the current @ref basic_json, or a different @ref basic_json convertible + from the current @ref basic_json. + + - Otherwise the value is converted by calling the @ref json_serializer `from_json()` + method. + + @tparam ValueTypeCV the provided value type + @tparam ValueType the returned value type + + @return copy of the JSON value, converted to @tparam ValueType if necessary + + @throw what @ref json_serializer `from_json()` method throws if conversion is required + + @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> {})) + { + // 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 JSONSerializer::from_json(*this); + return get_impl(detail::priority_tag<4> {}); + } + + /*! + @brief get a pointer value (explicit) + + Explicit pointer access to the internally stored JSON value. No copies are + made. + + @warning The pointer becomes invalid if the underlying JSON object + changes. + + @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. + + @return pointer to the internally stored JSON value if the requested + pointer type @a PointerType fits to the JSON value; `nullptr` otherwise + + @complexity Constant. + + @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__PointerType} + + @sa @ref get_ptr() for explicit pointer-member access + + @since version 1.0.0 + */ + template::value, int>::type = 0> + auto get() noexcept -> decltype(std::declval().template get_ptr()) + { + // delegate the call to get_ptr + return get_ptr(); } /*! @@ -19793,101 +19936,6 @@ class basic_json return v; } - - /*! - @brief get a pointer value (implicit) - - Implicit pointer access to the internally stored JSON value. No copies are - made. - - @warning Writing data to the pointee of the result yields an undefined - state. - - @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. - - @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::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)); - } - - /*! - @brief get a pointer value (implicit) - @copydoc get_ptr() - */ - 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())) - { - // delegate the call to get_impl_ptr<>() const - return get_impl_ptr(static_cast(nullptr)); - } - - /*! - @brief get a pointer value (explicit) - - Explicit pointer access to the internally stored JSON value. No copies are - made. - - @warning The pointer becomes invalid if the underlying JSON object - changes. - - @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. - - @return pointer to the internally stored JSON value if the requested - pointer type @a PointerType fits to the JSON value; `nullptr` otherwise - - @complexity Constant. - - @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__PointerType} - - @sa @ref get_ptr() for explicit pointer-member access - - @since version 1.0.0 - */ - template::value, int>::type = 0> - auto get() noexcept -> decltype(std::declval().template get_ptr()) - { - // delegate the call to get_ptr - return get_ptr(); - } - - /*! - @brief get a pointer value (explicit) - @copydoc get() - */ - template::value, int>::type = 0> - constexpr auto get() const noexcept -> decltype(std::declval().template get_ptr()) - { - // delegate the call to get_ptr - return get_ptr(); - } - /*! @brief get a reference value (implicit) From fbf6df63d9e16171abdcf47b6d699a1b19aaf1f9 Mon Sep 17 00:00:00 2001 From: Anthony VH Date: Wed, 13 Jan 2021 00:12:51 +0100 Subject: [PATCH 10/32] 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 500acb36e..a7adb724f 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 004850c3a..fd01826b4 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/32] Merged from_json for pair and tuple to try to fix C2995 error in old MSVC versions. --- .../nlohmann/detail/conversions/from_json.hpp | 57 ++++++++----------- single_include/nlohmann/json.hpp | 57 ++++++++----------- 2 files changed, 50 insertions(+), 64 deletions(-) diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index fcb6a5e60..9523943ca 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 < 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 -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> {})) -{ - 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()...); } +template < typename BasicJsonType, class A1, class A2 > +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_tuple_impl(BasicJsonType&& j, std::pair& p, priority_tag<1> /*unused*/) +{ + 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 fd01826b4..f7ee9030b 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 < 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 -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> {})) -{ - 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()...); } +template < typename BasicJsonType, class A1, class A2 > +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_tuple_impl(BasicJsonType&& j, std::pair& p, priority_tag<1> /*unused*/) +{ + 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/32] 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 a7adb724f..e5a7917c5 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 f7ee9030b..7ce313829 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/32] 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 1dee29eb9..f967612db 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 1fbf7cde7..631829d07 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 7ce313829..79c822d55 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 e31296fac..5a3de09d2 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/32] 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 631829d07..22d0bfe04 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 79c822d55..7abc0f9b7 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/32] 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 6ced652e1..62223ed28 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/32] 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 416ca16c5..f9873f5f4 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/32] 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 c661dd66e..4e4efd0a6 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 62223ed28..6fde2fd8c 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 f9873f5f4..4d48e4765 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()); } From e8dbd7be86e3dbbacd9c8cd4dd8c7a2340217d91 Mon Sep 17 00:00:00 2001 From: raduteo <57729561+raduteo@users.noreply.github.com> Date: Fri, 9 Apr 2021 09:35:58 -0400 Subject: [PATCH 18/32] Update json.hpp Proposed fix for #2706 --- single_include/nlohmann/json.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 4493dd654..7ef4befb2 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -18145,9 +18145,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec })); } JSON_CATCH(...) {} // LCOV_EXCL_LINE -#else - static_cast(check_parents); #endif + static_cast(check_parents); } void set_parents() From 35d79203ecafc0fbb5578c037184990512cf9fff Mon Sep 17 00:00:00 2001 From: raduteo <57729561+raduteo@users.noreply.github.com> Date: Mon, 12 Apr 2021 19:47:02 -0400 Subject: [PATCH 19/32] Update json.hpp --- include/nlohmann/json.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 3c9fa9199..06ff30329 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -1256,9 +1256,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec })); } JSON_CATCH(...) {} // LCOV_EXCL_LINE -#else - static_cast(check_parents); #endif + static_cast(check_parents); } void set_parents() From 31c4c8f36c835003e876c3f9daa18e235a43fca4 Mon Sep 17 00:00:00 2001 From: David Pfahler Date: Wed, 21 Apr 2021 11:03:28 +0200 Subject: [PATCH 20/32] Fixes #2730 Added define JSON_VERSION_IS_PREDEFINED for skipping automatic JSON_HAS_CPP_* detection --- include/nlohmann/detail/macro_scope.hpp | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index bd36048f1..111b4eeef 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -20,15 +20,18 @@ #endif // C++ language standard detection -#if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) - #define JSON_HAS_CPP_20 - #define JSON_HAS_CPP_17 - #define JSON_HAS_CPP_14 -#elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 - #define JSON_HAS_CPP_17 - #define JSON_HAS_CPP_14 -#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) - #define JSON_HAS_CPP_14 +// if the user wants to manually specify the used c++ version this is skipped +#ifndef JSON_VERSION_IS_PREDEFINED + #if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) + #define JSON_HAS_CPP_20 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) + #define JSON_HAS_CPP_14 + #endif #endif // disable documentation warnings on clang From 38952643c5b64e7d22f3259ae39550de176f1b51 Mon Sep 17 00:00:00 2001 From: David Pfahler Date: Wed, 21 Apr 2021 12:46:37 +0200 Subject: [PATCH 21/32] generate amalgation to fix CI for #2730 --- single_include/nlohmann/json.hpp | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 4493dd654..4af5a62f4 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -2229,15 +2229,18 @@ JSON_HEDLEY_DIAGNOSTIC_POP #endif // C++ language standard detection -#if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) - #define JSON_HAS_CPP_20 - #define JSON_HAS_CPP_17 - #define JSON_HAS_CPP_14 -#elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 - #define JSON_HAS_CPP_17 - #define JSON_HAS_CPP_14 -#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) - #define JSON_HAS_CPP_14 +// if the user wants to manually specify the used c++ version this is skipped +#ifndef JSON_VERSION_IS_PREDEFINED + #if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) + #define JSON_HAS_CPP_20 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) + #define JSON_HAS_CPP_14 + #endif #endif // disable documentation warnings on clang @@ -17094,7 +17097,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec detail::parser_callback_tcb = nullptr, const bool allow_exceptions = true, const bool ignore_comments = false - ) + ) { return ::nlohmann::detail::parser(std::move(adapter), std::move(cb), allow_exceptions, ignore_comments); @@ -25748,7 +25751,7 @@ template<> inline void swap(nlohmann::json& j1, nlohmann::json& j2) noexcept( // NOLINT(readability-inconsistent-declaration-parameter-name) is_nothrow_move_constructible::value&& // NOLINT(misc-redundant-expression) is_nothrow_move_assignable::value - ) +) { j1.swap(j2); } From 6b267e3cb15b7422bf1e4409774d87ee667b5fe4 Mon Sep 17 00:00:00 2001 From: David Pfahler Date: Wed, 21 Apr 2021 12:53:58 +0200 Subject: [PATCH 22/32] fixed amalgation file for #2730 --- single_include/nlohmann/json.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 4af5a62f4..3fdf8d686 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -17097,7 +17097,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec detail::parser_callback_tcb = nullptr, const bool allow_exceptions = true, const bool ignore_comments = false - ) + ) { return ::nlohmann::detail::parser(std::move(adapter), std::move(cb), allow_exceptions, ignore_comments); @@ -25751,7 +25751,7 @@ template<> inline void swap(nlohmann::json& j1, nlohmann::json& j2) noexcept( // NOLINT(readability-inconsistent-declaration-parameter-name) is_nothrow_move_constructible::value&& // NOLINT(misc-redundant-expression) is_nothrow_move_assignable::value -) + ) { j1.swap(j2); } From 10fc5162e7130fd04872164303b8eacdf3168cd4 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 24 Apr 2021 13:24:36 +0200 Subject: [PATCH 23/32] :arrow_up: use Clang 12 where available --- cmake/ci.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/ci.cmake b/cmake/ci.cmake index 5807d8f18..785d5f8d6 100644 --- a/cmake/ci.cmake +++ b/cmake/ci.cmake @@ -13,12 +13,12 @@ execute_process(COMMAND ${ASTYLE_TOOL} --version OUTPUT_VARIABLE ASTYLE_TOOL_VER string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" ASTYLE_TOOL_VERSION "${ASTYLE_TOOL_VERSION}") message(STATUS "🔖 Artistic Style ${ASTYLE_TOOL_VERSION} (${ASTYLE_TOOL})") -find_program(CLANG_TOOL NAMES clang++-HEAD clang++-11 clang++) +find_program(CLANG_TOOL NAMES clang++-HEAD clang++-12 clang++-11 clang++) execute_process(COMMAND ${CLANG_TOOL} --version OUTPUT_VARIABLE CLANG_TOOL_VERSION ERROR_VARIABLE CLANG_TOOL_VERSION) string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" CLANG_TOOL_VERSION "${CLANG_TOOL_VERSION}") message(STATUS "🔖 Clang ${CLANG_TOOL_VERSION} (${CLANG_TOOL})") -find_program(CLANG_TIDY_TOOL NAMES clang-tidy-11 clang-tidy) +find_program(CLANG_TIDY_TOOL NAMES clang-tidy-12 clang-tidy-11 clang-tidy) execute_process(COMMAND ${CLANG_TIDY_TOOL} --version OUTPUT_VARIABLE CLANG_TIDY_TOOL_VERSION ERROR_VARIABLE CLANG_TIDY_TOOL_VERSION) string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" CLANG_TIDY_TOOL_VERSION "${CLANG_TIDY_TOOL_VERSION}") message(STATUS "🔖 Clang-Tidy ${CLANG_TIDY_TOOL_VERSION} (${CLANG_TIDY_TOOL})") From 0638a27ed35d47cf2aa9a96ce0c104fb14cb072d Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 24 Apr 2021 13:50:30 +0200 Subject: [PATCH 24/32] :construction_worker: add step for Infer --- .github/workflows/ubuntu.yml | 2 +- cmake/ci.cmake | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 95073fa11..6e324a0d9 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -44,7 +44,7 @@ jobs: container: nlohmann/json-ci:latest strategy: matrix: - target: [ci_clang_tidy, ci_cppcheck, ci_test_valgrind, ci_test_clang_sanitizer, ci_test_amalgamation, ci_clang_analyze, ci_cpplint, ci_cmake_flags, ci_single_binaries, ci_reproducible_tests, ci_non_git_tests, ci_offline_testdata] + target: [ci_clang_tidy, ci_cppcheck, ci_test_valgrind, ci_test_clang_sanitizer, ci_test_amalgamation, ci_clang_analyze, ci_cpplint, ci_cmake_flags, ci_single_binaries, ci_reproducible_tests, ci_non_git_tests, ci_offline_testdata, ci_infer] steps: - uses: actions/checkout@v2 - name: cmake diff --git a/cmake/ci.cmake b/cmake/ci.cmake index 785d5f8d6..3d546359f 100644 --- a/cmake/ci.cmake +++ b/cmake/ci.cmake @@ -637,7 +637,7 @@ add_custom_target(ci_pvs_studio add_custom_target(ci_infer COMMAND mkdir -p ${PROJECT_BINARY_DIR}/build_infer COMMAND cd ${PROJECT_BINARY_DIR}/build_infer && ${INFER_TOOL} compile -- ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=Debug ${PROJECT_SOURCE_DIR} -DJSON_BuildTests=ON -DJSON_MultipleHeaders=ON - COMMAND cd ${PROJECT_BINARY_DIR}/build_infer && ${INFER_TOOL} run -- make --parallel ${N} + COMMAND cd ${PROJECT_BINARY_DIR}/build_infer && ${INFER_TOOL} run -- make COMMENT "Check code with Infer" ) From 608fbffcb1582b24a5c3247bdf5e760cbe35abb8 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 24 Apr 2021 13:51:53 +0200 Subject: [PATCH 25/32] :memo: update compiler versions --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 81d3a3016..bb931803c 100644 --- a/README.md +++ b/README.md @@ -1201,8 +1201,8 @@ auto cbor = json::to_msgpack(j); // 0xD5 (fixext2), 0x10, 0xCA, 0xFE Though it's 2021 already, the support for C++11 is still a bit sparse. Currently, the following compilers are known to work: - GCC 4.8 - 11.0 (and possibly later) -- Clang 3.4 - 11.0 (and possibly later) -- Apple Clang 9.1 - 12.3 (and possibly later) +- Clang 3.4 - 12.0 (and possibly later) +- Apple Clang 9.1 - 12.4 (and possibly later) - Intel C++ Compiler 17.0.2 (and possibly later) - Microsoft Visual C++ 2015 / Build Tools 14.0.25123.0 (and possibly later) - Microsoft Visual C++ 2017 / Build Tools 15.5.180.51428 (and possibly later) @@ -1271,7 +1271,8 @@ The following compilers are currently used in continuous integration at [Travis] | Clang 10.0.0 with GNU-like command-line | Windows-10.0.17763 | GitHub Actions | | Clang 11.0.0 with GNU-like command-line | Windows-10.0.17763 | GitHub Actions | | Clang 11.0.0 with MSVC-like command-line | Windows-10.0.17763 | GitHub Actions | -| Clang 11.1.0 (11.1.0-++20210204121720+1fdec59bffc1-1~exp1~20210203232336.162 | Ubuntu 20.04.2 LTS | GitHub Actions | +| Clang 11.0.0 (11.0.0-2~ubuntu20.04.1) | Ubuntu 20.04.2 LTS | GitHub Actions | +| Clang 12.1.0 (12.0.1-++20210423082613+072c90a863aa-1~exp1~20210423063319.76 | Ubuntu 20.04.2 LTS | GitHub Actions | | Visual Studio 14 2015 MSVC 19.0.24241.7 (Build Engine version 14.0.25420.1) | Windows-6.3.9600 | AppVeyor | | Visual Studio 15 2017 MSVC 19.16.27035.0 (Build Engine version 15.9.21+g9802d43bc3 for .NET Framework) | Windows-10.0.14393 | AppVeyor | | Visual Studio 15 2017 MSVC 19.16.27045.0 (Build Engine version 15.9.21+g9802d43bc3 for .NET Framework) | Windows-10.0.14393 | GitHub Actions | From 52717b74736e50ee4e108539624bd7a567e98d27 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 24 Apr 2021 14:27:13 +0200 Subject: [PATCH 26/32] :construction_worker: add clang 11 --- .github/workflows/ubuntu.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 6e324a0d9..fec68370a 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -90,7 +90,7 @@ jobs: container: nlohmann/json-ci:latest strategy: matrix: - compiler: [g++-4.8, g++-4.9, g++-5, g++-7, g++-8, g++-9, g++-10, clang++-3.5, clang++-3.6, clang++-3.7, clang++-3.8, clang++-3.9, clang++-4.0, clang++-5.0, clang++-6.0, clang++-7, clang++-8, clang++-9, clang++-10] + compiler: [g++-4.8, g++-4.9, g++-5, g++-7, g++-8, g++-9, g++-10, clang++-3.5, clang++-3.6, clang++-3.7, clang++-3.8, clang++-3.9, clang++-4.0, clang++-5.0, clang++-6.0, clang++-7, clang++-8, clang++-9, clang++-10, clang++-11] steps: - uses: actions/checkout@v2 - name: cmake From f6e9654aaaec89700e9afdaa1e7ea70904683c63 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 25 Apr 2021 14:33:12 +0200 Subject: [PATCH 27/32] :mute: suppress unsigned integer warnings --- cmake/ci.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/ci.cmake b/cmake/ci.cmake index 3d546359f..d7d94b2bf 100644 --- a/cmake/ci.cmake +++ b/cmake/ci.cmake @@ -486,7 +486,7 @@ add_custom_target(ci_test_coverage # Sanitizers. ############################################################################### -set(CLANG_CXX_FLAGS_SANITIZER "-g -O1 -fsanitize=address -fsanitize=undefined -fsanitize=integer -fsanitize=nullability -fno-omit-frame-pointer -fno-sanitize-recover=all -fsanitize-recover=unsigned-integer-overflow") +set(CLANG_CXX_FLAGS_SANITIZER "-g -O1 -fsanitize=address -fsanitize=undefined -fsanitize=integer -fsanitize=nullability -fno-omit-frame-pointer -fno-sanitize-recover=all -fno-sanitize=unsigned-integer-overflow -fno-sanitize=unsigned-shift-base") add_custom_target(ci_test_clang_sanitizer COMMAND CXX=${CLANG_TOOL} CXXFLAGS=${CLANG_CXX_FLAGS_SANITIZER} ${CMAKE_COMMAND} From 5a77314c5e786964c52ab7cd84e15f548fe04b33 Mon Sep 17 00:00:00 2001 From: David Pfahler Date: Mon, 26 Apr 2021 08:07:25 +0200 Subject: [PATCH 28/32] replaced define JSON_VERSION_IS_PREDEFINED with JSON_HAS_CPP_11 --- include/nlohmann/detail/macro_scope.hpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index 111b4eeef..663c2fbd3 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -20,8 +20,8 @@ #endif // C++ language standard detection -// if the user wants to manually specify the used c++ version this is skipped -#ifndef JSON_VERSION_IS_PREDEFINED +// if the user manually specified the used c++ version this is skipped +#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11) #if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) #define JSON_HAS_CPP_20 #define JSON_HAS_CPP_17 @@ -32,6 +32,8 @@ #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) #define JSON_HAS_CPP_14 #endif + // the cpp 11 flag is always specified because it is the minimal required version + #define JSON_HAS_CPP_11 #endif // disable documentation warnings on clang From d140a1c777b73198745f66577a4dc4fb08b0f135 Mon Sep 17 00:00:00 2001 From: David Pfahler Date: Mon, 26 Apr 2021 08:39:24 +0200 Subject: [PATCH 29/32] fixed amalgamation file --- single_include/nlohmann/json.hpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 3fdf8d686..62c6c485a 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -2229,8 +2229,8 @@ JSON_HEDLEY_DIAGNOSTIC_POP #endif // C++ language standard detection -// if the user wants to manually specify the used c++ version this is skipped -#ifndef JSON_VERSION_IS_PREDEFINED +// if the user manually specified the used c++ version this is skipped +#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11) #if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) #define JSON_HAS_CPP_20 #define JSON_HAS_CPP_17 @@ -2241,6 +2241,8 @@ JSON_HEDLEY_DIAGNOSTIC_POP #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) #define JSON_HAS_CPP_14 #endif + // the cpp 11 flag is always specified because it is the minimal required version + #define JSON_HAS_CPP_11 #endif // disable documentation warnings on clang From cdfe86548673808042d8abe1dc61e8a86b87dc58 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 28 Apr 2021 20:33:05 +0200 Subject: [PATCH 30/32] :memo: add documentation for numbers --- .github/CONTRIBUTING.md | 2 +- doc/images/json_syntax_number.png | Bin 0 -> 37014 bytes doc/mkdocs/Makefile | 2 +- doc/mkdocs/docs/api/basic_json/array_t.md | 4 +- doc/mkdocs/docs/api/basic_json/boolean_t.md | 2 +- .../docs/api/basic_json/number_float_t.md | 4 +- .../docs/api/basic_json/number_integer_t.md | 6 +- .../docs/api/basic_json/number_unsigned_t.md | 6 +- doc/mkdocs/docs/api/basic_json/object_t.md | 6 +- doc/mkdocs/docs/api/basic_json/string_t.md | 4 +- doc/mkdocs/docs/features/iterators.md | 2 +- .../features/{types.md => types/index.md} | 28 +- .../docs/features/types/number_handling.md | 313 ++++++++++++++++++ doc/mkdocs/docs/home/releases.md | 2 +- doc/mkdocs/mkdocs.yml | 6 +- include/nlohmann/detail/input/lexer.hpp | 6 +- include/nlohmann/json.hpp | 36 +- single_include/nlohmann/json.hpp | 42 +-- test/src/unit-class_parser.cpp | 8 +- test/src/unit-testsuites.cpp | 4 +- 20 files changed, 400 insertions(+), 83 deletions(-) create mode 100644 doc/images/json_syntax_number.png rename doc/mkdocs/docs/features/{types.md => types/index.md} (91%) create mode 100644 doc/mkdocs/docs/features/types/number_handling.md diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 7f12d5070..a9c0b4692 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -58,7 +58,7 @@ To make changes, you need to edit the following files: - Specifically, I am aware of compilation problems with **Microsoft Visual Studio** (there even is an [issue label](https://github.com/nlohmann/json/issues?utf8=✓&q=label%3A%22visual+studio%22+) for these kind of bugs). I understand that even in 2016, complete C++11 support isn't there yet. But please also understand that I do not want to drop features or uglify the code just to make Microsoft's sub-standard compiler happy. The past has shown that there are ways to express the functionality such that the code compiles with the most recent MSVC - unfortunately, this is not the main objective of the project. - Please refrain from proposing changes that would **break [JSON](https://json.org) conformance**. If you propose a conformant extension of JSON to be supported by the library, please motivate this extension. - We shall not extend the library to **support comments**. There is quite some [controversy](https://www.reddit.com/r/programming/comments/4v6chu/why_json_doesnt_support_comments_douglas_crockford/) around this topic, and there were quite some [issues](https://github.com/nlohmann/json/issues/376) on this. We believe that JSON is fine without comments. - - We do not preserve the **insertion order of object elements**. The [JSON standard](https://tools.ietf.org/html/rfc7159.html) defines objects as "an unordered collection of zero or more name/value pairs". To this end, this library does not preserve insertion order of name/value pairs. (In fact, keys will be traversed in alphabetical order as `std::map` with `std::less` is used by default.) Note this behavior conforms to the standard, and we shall not change it to any other order. If you do want to preserve the insertion order, you can specialize the object type with containers like [`tsl::ordered_map`](https://github.com/Tessil/ordered-map) or [`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map). + - We do not preserve the **insertion order of object elements**. The [JSON standard](https://tools.ietf.org/html/rfc8259.html) defines objects as "an unordered collection of zero or more name/value pairs". To this end, this library does not preserve insertion order of name/value pairs. (In fact, keys will be traversed in alphabetical order as `std::map` with `std::less` is used by default.) Note this behavior conforms to the standard, and we shall not change it to any other order. If you do want to preserve the insertion order, you can specialize the object type with containers like [`tsl::ordered_map`](https://github.com/Tessil/ordered-map) or [`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map). - Please do not open pull requests that address **multiple issues**. diff --git a/doc/images/json_syntax_number.png b/doc/images/json_syntax_number.png new file mode 100644 index 0000000000000000000000000000000000000000..be23ffa69d02833fb3b109b6a8c3e50e9701e2ff GIT binary patch literal 37014 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�)(qmJGY<%4C?Rkp9U{5) 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 JRK_gue*uKEw#)zk literal 0 HcmV?d00001 diff --git a/doc/mkdocs/Makefile b/doc/mkdocs/Makefile index 85bc6a920..1f3ccf54e 100644 --- a/doc/mkdocs/Makefile +++ b/doc/mkdocs/Makefile @@ -12,7 +12,7 @@ prepare_files: clean # create subfolders mkdir docs/images docs/examples # copy images - cp -vr ../json.gif ../images/range-begin-end.svg ../images/range-rbegin-rend.svg ../images/callback_events.png docs/images + cp -vr ../json.gif ../images/range-begin-end.svg ../images/range-rbegin-rend.svg ../images/callback_events.png ../images/json_syntax_number.png docs/images # copy examples cp -vr ../examples/*.cpp ../examples/*.output docs/examples diff --git a/doc/mkdocs/docs/api/basic_json/array_t.md b/doc/mkdocs/docs/api/basic_json/array_t.md index 89e39dbf8..f84e645bc 100644 --- a/doc/mkdocs/docs/api/basic_json/array_t.md +++ b/doc/mkdocs/docs/api/basic_json/array_t.md @@ -6,7 +6,7 @@ using array_t = ArrayType>; The type used to store JSON arrays. -[RFC 7159](http://rfc7159.net/rfc7159) describes JSON arrays as follows: +[RFC 8259](https://tools.ietf.org/html/rfc8259) describes JSON arrays as follows: > An array is an ordered sequence of zero or more values. To store objects in C++, a type is defined by the template parameters explained below. @@ -35,7 +35,7 @@ std::vector< #### Limits -[RFC 7159](http://rfc7159.net/rfc7159) specifies: +[RFC 8259](https://tools.ietf.org/html/rfc8259) specifies: > An implementation may set limits on the maximum depth of nesting. In this class, the array's limit of nesting is not explicitly constrained. However, a maximum depth of nesting may be diff --git a/doc/mkdocs/docs/api/basic_json/boolean_t.md b/doc/mkdocs/docs/api/basic_json/boolean_t.md index 926a5f9f4..a61151410 100644 --- a/doc/mkdocs/docs/api/basic_json/boolean_t.md +++ b/doc/mkdocs/docs/api/basic_json/boolean_t.md @@ -6,7 +6,7 @@ using boolean_t = BooleanType; The type used to store JSON booleans. -[RFC 7159](http://rfc7159.net/rfc7159) implicitly describes a boolean as a type which differentiates the two literals +[RFC 8259](https://tools.ietf.org/html/rfc8259) implicitly describes a boolean as a type which differentiates the two literals `#!json true` and `#!json false`. To store objects in C++, a type is defined by the template parameter `BooleanType` which chooses the type to use. diff --git a/doc/mkdocs/docs/api/basic_json/number_float_t.md b/doc/mkdocs/docs/api/basic_json/number_float_t.md index c6785eb26..0d0182b80 100644 --- a/doc/mkdocs/docs/api/basic_json/number_float_t.md +++ b/doc/mkdocs/docs/api/basic_json/number_float_t.md @@ -6,7 +6,7 @@ using number_float_t = NumberFloatType; The type used to store JSON numbers (floating-point). -[RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: +[RFC 8259](https://tools.ietf.org/html/rfc8259) describes numbers as follows: > The representation of numbers is similar to that used in most programming languages. A number is represented in base > 10 using decimal digits. It contains an integer component that may be prefixed with an optional minus sign, which may > be followed by a fraction part and/or an exponent part. Leading zeros are not allowed. (...) Numeric values that @@ -35,7 +35,7 @@ With the default values for `NumberFloatType` (`double`), the default value for #### Limits -[RFC 7159](http://rfc7159.net/rfc7159) states: +[RFC 8259](https://tools.ietf.org/html/rfc8259) states: > This specification allows implementations to set limits on the range and precision of numbers accepted. Since software > that implements IEEE 754-2008 binary64 (double precision) numbers is generally available and widely used, good > interoperability can be achieved by implementations that expect no more precision or range than these provide, in the diff --git a/doc/mkdocs/docs/api/basic_json/number_integer_t.md b/doc/mkdocs/docs/api/basic_json/number_integer_t.md index c10cb7803..616b0b8a2 100644 --- a/doc/mkdocs/docs/api/basic_json/number_integer_t.md +++ b/doc/mkdocs/docs/api/basic_json/number_integer_t.md @@ -6,7 +6,7 @@ using number_integer_t = NumberIntegerType; The type used to store JSON numbers (integers). -[RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: +[RFC 8259](https://tools.ietf.org/html/rfc8259) describes numbers as follows: > The representation of numbers is similar to that used in most programming languages. A number is represented in base > 10 using decimal digits. It contains an integer component that may be prefixed with an optional minus sign, which may > be followed by a fraction part and/or an exponent part. Leading zeros are not allowed. (...) Numeric values that @@ -36,7 +36,7 @@ With the default values for `NumberIntegerType` (`std::int64_t`), the default va #### Limits -[RFC 7159](http://rfc7159.net/rfc7159) specifies: +[RFC 8259](https://tools.ietf.org/html/rfc8259) specifies: > An implementation may set limits on the range and precision of numbers. When the default type is used, the maximal integer number that can be stored is `9223372036854775807` (INT64_MAX) and @@ -44,7 +44,7 @@ the minimal integer number that can be stored is `-9223372036854775808` (INT64_M range will yield over/underflow when used in a constructor. During deserialization, too large or small integer numbers will be automatically be stored as [`number_unsigned_t`](number_unsigned_t.md) or [`number_float_t`](number_float_t.md). -[RFC 7159](http://rfc7159.net/rfc7159) further states: +[RFC 8259](https://tools.ietf.org/html/rfc8259) further states: > Note that when such software is used, numbers that are integers and are in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are > interoperable in the sense that implementations will agree exactly on their numeric values. diff --git a/doc/mkdocs/docs/api/basic_json/number_unsigned_t.md b/doc/mkdocs/docs/api/basic_json/number_unsigned_t.md index a28e25351..49f0e3178 100644 --- a/doc/mkdocs/docs/api/basic_json/number_unsigned_t.md +++ b/doc/mkdocs/docs/api/basic_json/number_unsigned_t.md @@ -6,7 +6,7 @@ using number_unsigned_t = NumberUnsignedType; The type used to store JSON numbers (unsigned). -[RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: +[RFC 8259](https://tools.ietf.org/html/rfc8259) describes numbers as follows: > The representation of numbers is similar to that used in most programming languages. A number is represented in base > 10 using decimal digits. It contains an integer component that may be prefixed with an optional minus sign, which may > be followed by a fraction part and/or an exponent part. Leading zeros are not allowed. (...) Numeric values that @@ -36,7 +36,7 @@ With the default values for `NumberUnsignedType` (`std::uint64_t`), the default #### Limits -[RFC 7159](http://rfc7159.net/rfc7159) specifies: +[RFC 8259](https://tools.ietf.org/html/rfc8259) specifies: > An implementation may set limits on the range and precision of numbers. When the default type is used, the maximal integer number that can be stored is `18446744073709551615` (UINT64_MAX) and @@ -44,7 +44,7 @@ the minimal integer number that can be stored is `0`. Integer numbers that are o when used in a constructor. During deserialization, too large or small integer numbers will be automatically be stored as [`number_integer_t`](number_integer_t.md) or [`number_float_t`](number_float_t.md). -[RFC 7159](http://rfc7159.net/rfc7159) further states: +[RFC 8259](https://tools.ietf.org/html/rfc8259) further states: > Note that when such software is used, numbers that are integers and are in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are > interoperable in the sense that implementations will agree exactly on their numeric values. diff --git a/doc/mkdocs/docs/api/basic_json/object_t.md b/doc/mkdocs/docs/api/basic_json/object_t.md index efc18c41f..e937d847e 100644 --- a/doc/mkdocs/docs/api/basic_json/object_t.md +++ b/doc/mkdocs/docs/api/basic_json/object_t.md @@ -9,7 +9,7 @@ using object_t = ObjectType An object is an unordered collection of zero or more name/value pairs, where a name is a string and a value is a > string, number, boolean, null, object, or array. @@ -73,7 +73,7 @@ behavior: #### Limits -[RFC 7159](http://rfc7159.net/rfc7159) specifies: +[RFC 8259](https://tools.ietf.org/html/rfc8259) specifies: > An implementation may set limits on the maximum depth of nesting. In this class, the object's limit of nesting is not explicitly constrained. However, a maximum depth of nesting may be @@ -90,7 +90,7 @@ Objects are stored as pointers in a `basic_json` type. That is, for any access t The order name/value pairs are added to the object is *not* preserved by the library. Therefore, iterating an object may return name/value pairs in a different order than they were originally stored. In fact, keys will be traversed in alphabetical order as `std::map` with `std::less` is used by default. Please note this behavior conforms to -[RFC 7159](http://rfc7159.net/rfc7159), because any order implements the specified "unordered" nature of JSON objects. +[RFC 8259](https://tools.ietf.org/html/rfc8259), because any order implements the specified "unordered" nature of JSON objects. ## Version history diff --git a/doc/mkdocs/docs/api/basic_json/string_t.md b/doc/mkdocs/docs/api/basic_json/string_t.md index acdd351b5..f6287f894 100644 --- a/doc/mkdocs/docs/api/basic_json/string_t.md +++ b/doc/mkdocs/docs/api/basic_json/string_t.md @@ -6,7 +6,7 @@ using string_t = StringType; The type used to store JSON strings. -[RFC 7159](http://rfc7159.net/rfc7159) describes JSON strings as follows: +[RFC 8259](https://tools.ietf.org/html/rfc8259) describes JSON strings as follows: > A string is a sequence of zero or more Unicode characters. To store objects in C++, a type is defined by the template parameter described below. Unicode values are split by the @@ -31,7 +31,7 @@ the number of bytes in the string rather than the number of characters or glyphs #### String comparison -[RFC 7159](http://rfc7159.net/rfc7159) states: +[RFC 8259](https://tools.ietf.org/html/rfc8259) states: > Software implementations are typically required to test names of object members for equality. Implementations that > transform the textual representation into sequences of Unicode code units and then perform the comparison numerically, > code unit by code unit, are interoperable in the sense that implementations will agree in all cases on equality or diff --git a/doc/mkdocs/docs/features/iterators.md b/doc/mkdocs/docs/features/iterators.md index a8b88e2d8..fd9d6791a 100644 --- a/doc/mkdocs/docs/features/iterators.md +++ b/doc/mkdocs/docs/features/iterators.md @@ -10,7 +10,7 @@ As for other containers, `begin()` returns an iterator to the first value and `e ### Iteration order for objects -When iterating over objects, values are ordered with respect to the `object_comparator_t` type which defaults to `std::less`. See the [types documentation](types.md#key-order) for more information. +When iterating over objects, values are ordered with respect to the `object_comparator_t` type which defaults to `std::less`. See the [types documentation](types/index.md#key-order) for more information. ??? example diff --git a/doc/mkdocs/docs/features/types.md b/doc/mkdocs/docs/features/types/index.md similarity index 91% rename from doc/mkdocs/docs/features/types.md rename to doc/mkdocs/docs/features/types/index.md index 94e40cbaf..b2da9a911 100644 --- a/doc/mkdocs/docs/features/types.md +++ b/doc/mkdocs/docs/features/types/index.md @@ -1,4 +1,4 @@ -# Types +# Overview This page gives an overview how JSON values are stored and how this can be configured. @@ -107,7 +107,7 @@ using binary_t = nlohmann::byte_container_with_subtype; ## Objects -[RFC 7159](http://rfc7159.net/rfc7159) describes JSON objects as follows: +[RFC 8259](https://tools.ietf.org/html/rfc8259) describes JSON objects as follows: > An object is an unordered collection of zero or more name/value pairs, where a name is a string and a value is a string, number, boolean, null, object, or array. @@ -135,11 +135,11 @@ The choice of `object_t` influences the behavior of the JSON class. With the def ### Key order -The order name/value pairs are added to the object is *not* preserved by the library. Therefore, iterating an object may return name/value pairs in a different order than they were originally stored. In fact, keys will be traversed in alphabetical order as `std::map` with `std::less` is used by default. Please note this behavior conforms to [RFC 7159](http://rfc7159.net/rfc7159), because any order implements the specified "unordered" nature of JSON objects. +The order name/value pairs are added to the object is *not* preserved by the library. Therefore, iterating an object may return name/value pairs in a different order than they were originally stored. In fact, keys will be traversed in alphabetical order as `std::map` with `std::less` is used by default. Please note this behavior conforms to [RFC 8259](https://tools.ietf.org/html/rfc8259), because any order implements the specified "unordered" nature of JSON objects. ### Limits -[RFC 7159](http://rfc7159.net/rfc7159) specifies: +[RFC 8259](https://tools.ietf.org/html/rfc8259) specifies: > An implementation may set limits on the maximum depth of nesting. @@ -152,7 +152,7 @@ Objects are stored as pointers in a `basic_json` type. That is, for any access t ## Arrays -[RFC 7159](http://rfc7159.net/rfc7159) describes JSON arrays as follows: +[RFC 8259](https://tools.ietf.org/html/rfc8259) describes JSON arrays as follows: > An array is an ordered sequence of zero or more values. @@ -169,7 +169,7 @@ std::vector< ### Limits -[RFC 7159](http://rfc7159.net/rfc7159) specifies: +[RFC 8259](https://tools.ietf.org/html/rfc8259) specifies: > An implementation may set limits on the maximum depth of nesting. @@ -182,7 +182,7 @@ Arrays are stored as pointers in a `basic_json` type. That is, for any access to ## Strings -[RFC 7159](http://rfc7159.net/rfc7159) describes JSON strings as follows: +[RFC 8259](https://tools.ietf.org/html/rfc8259) describes JSON strings as follows: > A string is a sequence of zero or more Unicode characters. @@ -198,7 +198,7 @@ Strings are stored in UTF-8 encoding. Therefore, functions like `std::string::si ### String comparison -[RFC 7159](http://rfc7159.net/rfc7159) states: +[RFC 8259](https://tools.ietf.org/html/rfc8259) states: > Software implementations are typically required to test names of object members for equality. Implementations that transform the textual representation into sequences of Unicode code units and then perform the comparison numerically, code unit by code unit, are interoperable in the sense that implementations will agree in all cases on equality or inequality of two strings. For example, implementations that compare strings with escaped characters unconverted may incorrectly find that `"a\\b"` and `"a\u005Cb"` are not equal. @@ -211,7 +211,7 @@ String values are stored as pointers in a `basic_json` type. That is, for any ac ## Booleans -[RFC 7159](http://rfc7159.net/rfc7159) implicitly describes a boolean as a type which differentiates the two literals `true` and `false`. +[RFC 8259](https://tools.ietf.org/html/rfc8259) implicitly describes a boolean as a type which differentiates the two literals `true` and `false`. ### Default type @@ -223,7 +223,9 @@ Boolean values are stored directly inside a `basic_json` type. ## Numbers -[RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: +See the [number handling](number_handling.md) article for a detailed discussion on how numbers are handled by this library. + +[RFC 8259](https://tools.ietf.org/html/rfc8259) describes numbers as follows: > The representation of numbers is similar to that used in most programming languages. A number is represented in base 10 using decimal digits. It contains an integer component that may be prefixed with an optional minus sign, which may be followed by a fraction part and/or an exponent part. Leading zeros are not allowed. (...) Numeric values that cannot be represented in the grammar below (such as Infinity and NaN) are not permitted. @@ -242,7 +244,7 @@ With the default values for *NumberFloatType* (`#!cpp double`), the default valu ### Limits -[RFC 7159](http://rfc7159.net/rfc7159) specifies: +[RFC 8259](https://tools.ietf.org/html/rfc8259) specifies: > An implementation may set limits on the range and precision of numbers. @@ -250,13 +252,13 @@ When the default type is used, the maximal integer number that can be stored is When the default type is used, the maximal unsigned integer number that can be stored is `#!c 18446744073709551615` (`UINT64_MAX`) and the minimal integer number that can be stored is `#!c 0`. Integer numbers that are out of range will yield over/underflow when used in a constructor. During deserialization, too large or small integer numbers will be automatically be stored as `number_integer_t` or `number_float_t`. -[RFC 7159](http://rfc7159.net/rfc7159) further states: +[RFC 8259](https://tools.ietf.org/html/rfc8259) further states: > Note that when such software is used, numbers that are integers and are in the range $[-2^{53}+1, 2^{53}-1]$ are interoperable in the sense that implementations will agree exactly on their numeric values. As this range is a subrange of the exactly supported range [`INT64_MIN`, `INT64_MAX`], this class's integer type is interoperable. -[RFC 7159](http://rfc7159.net/rfc7159) states: +[RFC 8259](https://tools.ietf.org/html/rfc8259) states: > This specification allows implementations to set limits on the range and precision of numbers accepted. Since software that implements IEEE 754-2008 binary64 (double precision) numbers is generally available and widely used, good interoperability can be achieved by implementations that expect no more precision or range than these provide, in the sense that implementations will approximate JSON numbers within the expected precision. diff --git a/doc/mkdocs/docs/features/types/number_handling.md b/doc/mkdocs/docs/features/types/number_handling.md new file mode 100644 index 000000000..4224c155b --- /dev/null +++ b/doc/mkdocs/docs/features/types/number_handling.md @@ -0,0 +1,313 @@ +# Number Handling + +This document describes how the library is handling numbers. + +## Background + +This section briefly summarizes how the JSON specification describes how numbers should be handled. + +### JSON number syntax + +JSON defines the syntax of numbers as follows: + +!!! quote "[RFC 8259](https://tools.ietf.org/html/rfc8259#section-6), Section 6" + + The representation of numbers is similar to that used in most + programming languages. A number is represented in base 10 using + decimal digits. It contains an integer component that may be + prefixed with an optional minus sign, which may be followed by a + fraction part and/or an exponent part. Leading zeros are not + allowed. + + A fraction part is a decimal point followed by one or more digits. + + An exponent part begins with the letter E in uppercase or lowercase, + which may be followed by a plus or minus sign. The E and optional + sign are followed by one or more digits. + +The following railroad diagram from [json.org](https://json.org) visualizes the number syntax: + +![Syntax for JSON numbers](../../images/json_syntax_number.png) + +### Number interoperability + +On number interoperability, the following remarks are made: + +!!! quote "[RFC 8259](https://tools.ietf.org/html/rfc8259#section-6), Section 6" + + This specification allows implementations to set limits on the range + and precision of numbers accepted. Since software that implements + IEEE 754 binary64 (double precision) numbers [IEEE754] is generally + available and widely used, good interoperability can be achieved by + implementations that expect no more precision or range than these + provide, in the sense that implementations will approximate JSON + numbers within the expected precision. A JSON number such as 1E400 + or 3.141592653589793238462643383279 may indicate potential + interoperability problems, since it suggests that the software that + created it expects receiving software to have greater capabilities + for numeric magnitude and precision than is widely available. + + Note that when such software is used, numbers that are integers and + are in the range $[-2^{53}+1, 2^{53}-1]$ are interoperable in the + sense that implementations will agree exactly on their numeric + values. + +## Library implementation + +This section describes how the above number specification is implemented by this library. + +### Number storage + +In the default [`json`](../../api/json.md) type, numbers are stored as `#!c std::uint64_t`, `#!c std::int64_t`, and +`#!c double`, respectively. Thereby, `#!c std::uint64_t` and `#!c std::int64_t` are used only if they can store the +number without loss of precision. If this is impossible (e.g., if the number is too large), the number is stored as +`#!c double`. + +!!! info "Notes" + + - Numbers with a decimal digit or scientific notation are always stored as `#!c double`. + - The number types can be changed, see [Template number types](#template-number-types). + - As of version 3.9.1, the conversion is realized by + [`std::strtoull`](https://en.cppreference.com/w/cpp/string/byte/strtoul), + [`std::strtoll`](https://en.cppreference.com/w/cpp/string/byte/strtol), and + [`std::strtod`](https://en.cppreference.com/w/cpp/string/byte/strtof), respectively. + +!!! example "Examples" + + - Integer `#!c -12345678912345789123456789` is smaller than `#!c INT64_MIN` and will be stored as floating-point + number `#!c -1.2345678912345788e+25`. + - Integer `#!c 1E3` will be stored as floating-point number `#!c 1000.0`. + +### Number limits + +- Any 64 bit signed or unsigned integer can be stored without loss of precision. +- Numbers exceeding the limits of `#!c double` (i.e., numbers that after conversion via +[`std::strtod`](https://en.cppreference.com/w/cpp/string/byte/strtof) are not satisfying +[`std::isfinite`](https://en.cppreference.com/w/cpp/numeric/math/isfinite) such as `#!c 1E400`) will throw exception +[`json.exception.out_of_range.406`](../../home/exceptions.md#jsonexceptionout_of_range406) during parsing. +- Floating-point numbers are rounded to the next number representable as `double`. For instance +`#!c 3.141592653589793238462643383279` is stored as [`0x400921fb54442d18`](https://float.exposed/0x400921fb54442d18). +This is the same behavior as the code `#!c double x = 3.141592653589793238462643383279;`. + +!!! success "Interoperability" + + - The library interoperable with respect to the specification, because its supported range $[-2^{63}, 2^{64}-1]$ is + larger than the described range $[-2^{53}+1, 2^{53}-1]$. + - All integers outside the range $[-2^{63}, 2^{64}-1]$, as well as floating-point numbers are stored as `double`. + This also concurs with the specification above. + +### Number serialization + +- Integer numbers are serialized as is; that is, no scientific notation is used. +- Floating-point numbers are serialized as specified by the `#!c %g` printf modifier with + [`std::numeric_limits::max_digits10`](https://en.cppreference.com/w/cpp/types/numeric_limits/max_digits10) + significant digits). The rationale is to use the shortest representation while still allow round-tripping. + +!!! hint "Notes regarding precision of floating-point numbers" + + As described above, floating-point numbers are rounded to the nearest double and serialized with the shortest + representation to allow round-tripping. This can yield confusing examples: + + - The serialization can have fewer decimal places than the input: `#!c 2555.5599999999999` will be serialized as + `#!c 2555.56`. The reverse can also be true. + - The serialization can be in scientific notation even if the input is not: `#!c 0.0000972439793401814` will be + serialized as `#!c 9.72439793401814e-05`. The reverse can also be true: `#!c 12345E-5` will be serialized as + `#!c 0.12345`. + - Conversions from `#!c float` to `#!c double` can also introduce rouding errors: + ```cpp + float f = 0.3; + json j = f; + std::cout << j << '\n'; + ``` + yields `#!c 0.30000001192092896`. + + All examples here can be reproduced by passing the original double value to + + ```cpp + std::printf("%.*g\n", std::numeric_limits::max_digits10, double_value); + ``` + +#### NaN handling + +NaN (not-a-number) cannot be expressed with the number syntax described above and are in fact explicitly excluded: + +!!! quote "[RFC 8259](https://tools.ietf.org/html/rfc8259#section-6), Section 6" + + Numeric values that cannot be represented in the grammar below (such + as Infinity and NaN) are not permitted. + +That is, there is no way to *parse* a NaN value. However, NaN values can be stored in a JSON value by assignment. + +This library serializes NaN values as `#!js null`. This corresponds to the behavior of JavaScript's +[`JSON.stringify`](https://www.w3schools.com/js/js_json_stringify.asp) function. + +!!! example + + The following example shows how a NaN value is stored in a `json` value. + + ```cpp + int main() + { + double val = std::numeric_limits::quiet_NaN(); + std::cout << "val=" << val << std::endl; + json j = val; + std::cout << "j=" << j.dump() << std::endl; + val = j; + std::cout << "val=" << val << std::endl; + } + ``` + + output: + + ``` + val=nan + j=null + val=nan + ``` + +### Number comparison + +Floating-point inside JSON values numbers are compared with `#!c json::number_float_t::operator==` which is +`#!c double::operator==` by default. + +!!! example "Alternative comparison functions" + + To compare floating-point while respecting an epsilon, an alternative + [comparison function](https://github.com/mariokonrad/marnav/blob/master/include/marnav/math/floatingpoint.hpp#L34-#L39) + could be used, for instance + + ```cpp + template::value, T>::type> + inline bool is_same(T a, T b, T epsilon = std::numeric_limits::epsilon()) noexcept + { + return std::abs(a - b) <= epsilon; + } + ``` + Or you can self-define an operator equal function like this: + + ```cpp + bool my_equal(const_reference lhs, const_reference rhs) + { + const auto lhs_type lhs.type(); + const auto rhs_type rhs.type(); + if (lhs_type == rhs_type) + { + switch(lhs_type) + { + // self_defined case + case value_t::number_float: + return std::abs(lhs - rhs) <= std::numeric_limits::epsilon(); + + // other cases remain the same with the original + ... + } + } + ... + } + ``` + + (see [#703](https://github.com/nlohmann/json/issues/703) for more information.) + +!!! note + + NaN values never compare equal to themselves or to other NaN values. See [#514](https://github.com/nlohmann/json/issues/514). + +### Number conversion + +Just like the C++ language itself, the `get` family of functions allows conversions between unsigned and signed +integers, and between integers and floating-point values to integers. This behavior may be surprising. + +!!! warning "Unconditional number conversions" + + ```cpp hl_lines="3" + double d = 42.3; // non-integer double value 42.3 + json jd = d; // stores double value 42.3 + std::int64_t i = jd.get(); // now i==42; no warning or error is produced + ``` + + Note the last line with throw a [`json.exception.type_error.302`](../../home/exceptions.md#jsonexceptiontype_error302) + exception if `jd` is not a numerical type, for instance a string. + +The rationale is twofold: + +1. JSON does not define a number type or precision (see [#json-specification](above)). +2. C++ also allows to silently convert between number types. + +!!! success "Conditional number conversion" + + The code above can be solved by explicitly checking the nature of the value with members such as + [`is_number_integer()`](../../api/basic_json/is_number_integer.md) or + [`is_number_unsigned()`](../../api/basic_json/is_number_unsigned.md): + + ```cpp hl_lines="2" + // check if jd is really integer-valued + if (jd.is_number_integer()) + { + // if so, do the conversion and use i + std::int64_t i = jd.get(); + // ... + } + else + { + // otherwise, take appropriate action + // ... + } + ``` + + Note this approach also has the advantage that it can react on non-numerical JSON value types such as strings. + + (Example taken from [#777](https://github.com/nlohmann/json/issues/777#issuecomment-459968458).) + +### Determine number types + +As the example in [Number conversion](#number_conversion) shows, there are different functions to determine the type of +the stored number: + +- [`is_number()`](../../api/basic_json/is_number.md) returns `#!c true` for any number type +- [`is_number_integer()`](../../api/basic_json/is_number_integer.md) returns `#!c true` for signed and unsigned integers +- [`is_number_unsigned()`](../../api/basic_json/is_number_unsigned.md) returns `#!c true` for unsigned integers only +- [`is_number_float()`](../../api/basic_json/is_number_float.md) returns `#!c true` for floating-point numbers +- [`type_name()`](../../api/basic_json/type_name.md) returns `#!c "number"` for any number type +- [`type()`](../../api/basic_json/type.md) returns an different enumerator of + [`value_t`](../../api/basic_json/value_t.md) for all number types + +| function | unsigned integer | signed integer | floating-point | string | +| -------- | ---------------- | -------------- | -------------- | ------ | +| [`is_number()`](../../api/basic_json/is_number.md) | `#!c true` | `#!c true` | `#!c true` | `#!c false` | +| [`is_number_integer()`](../../api/basic_json/is_number_integer.md) | `#!c true` | `#!c true` | `#!c false` | `#!c false` | +| [`is_number_unsigned()`](../../api/basic_json/is_number_unsigned.md) | `#!c true` | `#!c false` | `#!c false` | `#!c false` | +| [`is_number_float()`](../../api/basic_json/is_number_float.md) | `#!c false` | `#!c false` | `#!c true` | `#!c false` | +| [`type_name()`](../../api/basic_json/type_name.md) | `#!c "number"` | `#!c "number"` | `#!c "number"` | `#!c "string"` | +| [`type()`](../../api/basic_json/type.md) | `number_unsigned` | `number_integer` | `number_float` | `string` | + +### Template number types + +The number types can be changed with template parameters. + +| position | number type | default type | possible values | +| -------- | ----------- | ------------ | --------------- | +| 5 | signed integers | `#!c std::int64_t` | `#!c std::int32_t`, `#!c std::int16_t`, etc. | +| 6 | unsigned integers | `#!c std::uint64_t` | `#!c std::uint32_t`, `#!c std::uint16_t`, etc. | +| 7 | floating-point | `#!c double` | `#!c float`, `#!c long double` | + +!!! info "Constraints on number types" + + - The type for signed integers must be convertible from `#!c long long`. The type for floating-point numbers is used + in case of overflow. + - The type for unsigned integers must be convertible from `#!c unsigned long long`. The type for floating-point + numbers is used in case of overflow. + - The types for signed and unsigned integers must be distinct, see + [#2573](https://github.com/nlohmann/json/issues/2573). + - Only `#!c double`, `#!c float`, and `#!c long double` are supported for floating-point numbers. + +!!! example + + A `basic_json` type that uses `#!c long double` as floating-point type. + + ```cpp hl_lines="2" + using json_ld = nlohmann::basic_json; + ``` + + Note values should then be parsed with `json_ld::parse` rather than `json::parse` as the latter would parse + floating-point values to `#!c double` before then converting them to `#!c long double`. diff --git a/doc/mkdocs/docs/home/releases.md b/doc/mkdocs/docs/home/releases.md index 0cb890b0a..298c3772b 100644 --- a/doc/mkdocs/docs/home/releases.md +++ b/doc/mkdocs/docs/home/releases.md @@ -1167,7 +1167,7 @@ As `noexcept` and `constexpr` specifier have been added to several functions, th - Parser error messages are still very vague and contain no information on the error location. - The implemented `diff` function is rather primitive and does not create minimal diffs. - The name of function `iteration_wrapper` may change in the future and the function will be deprecated in the next release. -- Roundtripping (i.e., parsing a JSON value from a string, serializing it, and comparing the strings) of floating-point numbers is not 100% accurate. Note that [RFC 7159](https://tools.ietf.org/html/rfc7159) defines no format to internally represent numbers and states not requirement for roundtripping. Nevertheless, benchmarks like [Native JSON Benchmark](https://github.com/miloyip/nativejson-benchmark) treat roundtripping deviations as conformance errors. +- Roundtripping (i.e., parsing a JSON value from a string, serializing it, and comparing the strings) of floating-point numbers is not 100% accurate. Note that [RFC 8259](https://tools.ietf.org/html/rfc8259) defines no format to internally represent numbers and states not requirement for roundtripping. Nevertheless, benchmarks like [Native JSON Benchmark](https://github.com/miloyip/nativejson-benchmark) treat roundtripping deviations as conformance errors. ## v1.1.0 diff --git a/doc/mkdocs/mkdocs.yml b/doc/mkdocs/mkdocs.yml index e1552d39b..b7ad380fe 100644 --- a/doc/mkdocs/mkdocs.yml +++ b/doc/mkdocs/mkdocs.yml @@ -9,7 +9,7 @@ repo_url: https://github.com/nlohmann/json edit_uri: edit/develop/doc/mkdocs/docs # Copyright -copyright: Copyright © 2013 - 2020 Niels Lohmann +copyright: Copyright © 2013 - 2021 Niels Lohmann # Configuration theme: @@ -62,7 +62,9 @@ nav: - features/parsing/sax_interface.md - features/enum_conversion.md - features/macros.md - - features/types.md + - Types: + - features/types/index.md + - features/types/number_handling.md - Integration: - integration/index.md - integration/cmake.md diff --git a/include/nlohmann/detail/input/lexer.hpp b/include/nlohmann/detail/input/lexer.hpp index 7db272261..3a47167e9 100644 --- a/include/nlohmann/detail/input/lexer.hpp +++ b/include/nlohmann/detail/input/lexer.hpp @@ -231,7 +231,7 @@ class lexer : public lexer_base /*! @brief scan a string literal - This function scans a string according to Sect. 7 of RFC 7159. While + This function scans a string according to Sect. 7 of RFC 8259. While scanning, bytes are escaped and copied into buffer token_buffer. Then the function returns successfully, token_buffer is *not* null-terminated (as it may contain \0 bytes), and token_buffer.size() is the number of bytes in the @@ -921,10 +921,10 @@ class lexer : public lexer_base /*! @brief scan a number literal - This function scans a string according to Sect. 6 of RFC 7159. + This function scans a string according to Sect. 6 of RFC 8259. The function is realized with a deterministic finite state machine derived - from the grammar described in RFC 7159. Starting in state "init", the + from the grammar described in RFC 8259. Starting in state "init", the input is read and used to determined the next state. Only state "done" accepts the number. State "error" is a trap state to model errors. In the table below, "anything" means any character but the ones listed before. diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 337b64c9d..0bbaa9503 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -163,8 +163,8 @@ The invariants are checked by member function assert_invariant(). @note ObjectType trick from https://stackoverflow.com/a/9860911 @endinternal -@see [RFC 7159: The JavaScript Object Notation (JSON) Data Interchange -Format](http://rfc7159.net/rfc7159) +@see [RFC 8259: The JavaScript Object Notation (JSON) Data Interchange +Format](https://tools.ietf.org/html/rfc8259) @since version 1.0.0 @@ -425,7 +425,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /*! @brief a type for an object - [RFC 7159](http://rfc7159.net/rfc7159) describes JSON objects as follows: + [RFC 8259](https://tools.ietf.org/html/rfc8259) describes JSON objects as follows: > An object is an unordered collection of zero or more name/value pairs, > where a name is a string and a value is a string, number, boolean, null, > object, or array. @@ -479,7 +479,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec #### Limits - [RFC 7159](http://rfc7159.net/rfc7159) specifies: + [RFC 8259](https://tools.ietf.org/html/rfc8259) specifies: > An implementation may set limits on the maximum depth of nesting. In this class, the object's limit of nesting is not explicitly constrained. @@ -502,7 +502,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec name/value pairs in a different order than they were originally stored. In fact, keys will be traversed in alphabetical order as `std::map` with `std::less` is used by default. Please note this behavior conforms to [RFC - 7159](http://rfc7159.net/rfc7159), because any order implements the + 8259](https://tools.ietf.org/html/rfc8259), because any order implements the specified "unordered" nature of JSON objects. */ using object_t = ObjectType An array is an ordered sequence of zero or more values. To store objects in C++, a type is defined by the template parameters @@ -538,7 +538,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec #### Limits - [RFC 7159](http://rfc7159.net/rfc7159) specifies: + [RFC 8259](https://tools.ietf.org/html/rfc8259) specifies: > An implementation may set limits on the maximum depth of nesting. In this class, the array's limit of nesting is not explicitly constrained. @@ -560,7 +560,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /*! @brief a type for a string - [RFC 7159](http://rfc7159.net/rfc7159) describes JSON strings as follows: + [RFC 8259](https://tools.ietf.org/html/rfc8259) describes JSON strings as follows: > A string is a sequence of zero or more Unicode characters. To store objects in C++, a type is defined by the template parameter @@ -587,7 +587,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec #### String comparison - [RFC 7159](http://rfc7159.net/rfc7159) states: + [RFC 8259](https://tools.ietf.org/html/rfc8259) states: > Software implementations are typically required to test names of object > members for equality. Implementations that transform the textual > representation into sequences of Unicode code units and then perform the @@ -613,7 +613,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /*! @brief a type for a boolean - [RFC 7159](http://rfc7159.net/rfc7159) implicitly describes a boolean as a + [RFC 8259](https://tools.ietf.org/html/rfc8259) implicitly describes a boolean as a type which differentiates the two literals `true` and `false`. To store objects in C++, a type is defined by the template parameter @a @@ -639,7 +639,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /*! @brief a type for a number (integer) - [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: + [RFC 8259](https://tools.ietf.org/html/rfc8259) describes numbers as follows: > The representation of numbers is similar to that used in most > programming languages. A number is represented in base 10 using decimal > digits. It contains an integer component that may be prefixed with an @@ -677,7 +677,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec #### Limits - [RFC 7159](http://rfc7159.net/rfc7159) specifies: + [RFC 8259](https://tools.ietf.org/html/rfc8259) specifies: > An implementation may set limits on the range and precision of numbers. When the default type is used, the maximal integer number that can be @@ -688,7 +688,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec will be automatically be stored as @ref number_unsigned_t or @ref number_float_t. - [RFC 7159](http://rfc7159.net/rfc7159) further states: + [RFC 8259](https://tools.ietf.org/html/rfc8259) further states: > Note that when such software is used, numbers that are integers and are > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense > that implementations will agree exactly on their numeric values. @@ -711,7 +711,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /*! @brief a type for a number (unsigned) - [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: + [RFC 8259](https://tools.ietf.org/html/rfc8259) describes numbers as follows: > The representation of numbers is similar to that used in most > programming languages. A number is represented in base 10 using decimal > digits. It contains an integer component that may be prefixed with an @@ -749,7 +749,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec #### Limits - [RFC 7159](http://rfc7159.net/rfc7159) specifies: + [RFC 8259](https://tools.ietf.org/html/rfc8259) specifies: > An implementation may set limits on the range and precision of numbers. When the default type is used, the maximal integer number that can be @@ -759,7 +759,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec deserialization, too large or small integer numbers will be automatically be stored as @ref number_integer_t or @ref number_float_t. - [RFC 7159](http://rfc7159.net/rfc7159) further states: + [RFC 8259](https://tools.ietf.org/html/rfc8259) further states: > Note that when such software is used, numbers that are integers and are > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense > that implementations will agree exactly on their numeric values. @@ -782,7 +782,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /*! @brief a type for a number (floating-point) - [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: + [RFC 8259](https://tools.ietf.org/html/rfc8259) describes numbers as follows: > The representation of numbers is similar to that used in most > programming languages. A number is represented in base 10 using decimal > digits. It contains an integer component that may be prefixed with an @@ -820,7 +820,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec #### Limits - [RFC 7159](http://rfc7159.net/rfc7159) states: + [RFC 8259](https://tools.ietf.org/html/rfc8259) states: > This specification allows implementations to set limits on the range and > precision of numbers accepted. Since software that implements IEEE > 754-2008 binary64 (double precision) numbers is generally available and diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 97f77b4fa..4ba979fd2 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -6649,7 +6649,7 @@ class lexer : public lexer_base /*! @brief scan a string literal - This function scans a string according to Sect. 7 of RFC 7159. While + This function scans a string according to Sect. 7 of RFC 8259. While scanning, bytes are escaped and copied into buffer token_buffer. Then the function returns successfully, token_buffer is *not* null-terminated (as it may contain \0 bytes), and token_buffer.size() is the number of bytes in the @@ -7339,10 +7339,10 @@ class lexer : public lexer_base /*! @brief scan a number literal - This function scans a string according to Sect. 6 of RFC 7159. + This function scans a string according to Sect. 6 of RFC 8259. The function is realized with a deterministic finite state machine derived - from the grammar described in RFC 7159. Starting in state "init", the + from the grammar described in RFC 8259. Starting in state "init", the input is read and used to determined the next state. Only state "done" accepts the number. State "error" is a trap state to model errors. In the table below, "anything" means any character but the ones listed before. @@ -17178,8 +17178,8 @@ The invariants are checked by member function assert_invariant(). @note ObjectType trick from https://stackoverflow.com/a/9860911 @endinternal -@see [RFC 7159: The JavaScript Object Notation (JSON) Data Interchange -Format](http://rfc7159.net/rfc7159) +@see [RFC 8259: The JavaScript Object Notation (JSON) Data Interchange +Format](https://tools.ietf.org/html/rfc8259) @since version 1.0.0 @@ -17440,7 +17440,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /*! @brief a type for an object - [RFC 7159](http://rfc7159.net/rfc7159) describes JSON objects as follows: + [RFC 8259](https://tools.ietf.org/html/rfc8259) describes JSON objects as follows: > An object is an unordered collection of zero or more name/value pairs, > where a name is a string and a value is a string, number, boolean, null, > object, or array. @@ -17494,7 +17494,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec #### Limits - [RFC 7159](http://rfc7159.net/rfc7159) specifies: + [RFC 8259](https://tools.ietf.org/html/rfc8259) specifies: > An implementation may set limits on the maximum depth of nesting. In this class, the object's limit of nesting is not explicitly constrained. @@ -17517,7 +17517,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec name/value pairs in a different order than they were originally stored. In fact, keys will be traversed in alphabetical order as `std::map` with `std::less` is used by default. Please note this behavior conforms to [RFC - 7159](http://rfc7159.net/rfc7159), because any order implements the + 8259](https://tools.ietf.org/html/rfc8259), because any order implements the specified "unordered" nature of JSON objects. */ using object_t = ObjectType An array is an ordered sequence of zero or more values. To store objects in C++, a type is defined by the template parameters @@ -17553,7 +17553,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec #### Limits - [RFC 7159](http://rfc7159.net/rfc7159) specifies: + [RFC 8259](https://tools.ietf.org/html/rfc8259) specifies: > An implementation may set limits on the maximum depth of nesting. In this class, the array's limit of nesting is not explicitly constrained. @@ -17575,7 +17575,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /*! @brief a type for a string - [RFC 7159](http://rfc7159.net/rfc7159) describes JSON strings as follows: + [RFC 8259](https://tools.ietf.org/html/rfc8259) describes JSON strings as follows: > A string is a sequence of zero or more Unicode characters. To store objects in C++, a type is defined by the template parameter @@ -17602,7 +17602,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec #### String comparison - [RFC 7159](http://rfc7159.net/rfc7159) states: + [RFC 8259](https://tools.ietf.org/html/rfc8259) states: > Software implementations are typically required to test names of object > members for equality. Implementations that transform the textual > representation into sequences of Unicode code units and then perform the @@ -17628,7 +17628,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /*! @brief a type for a boolean - [RFC 7159](http://rfc7159.net/rfc7159) implicitly describes a boolean as a + [RFC 8259](https://tools.ietf.org/html/rfc8259) implicitly describes a boolean as a type which differentiates the two literals `true` and `false`. To store objects in C++, a type is defined by the template parameter @a @@ -17654,7 +17654,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /*! @brief a type for a number (integer) - [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: + [RFC 8259](https://tools.ietf.org/html/rfc8259) describes numbers as follows: > The representation of numbers is similar to that used in most > programming languages. A number is represented in base 10 using decimal > digits. It contains an integer component that may be prefixed with an @@ -17692,7 +17692,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec #### Limits - [RFC 7159](http://rfc7159.net/rfc7159) specifies: + [RFC 8259](https://tools.ietf.org/html/rfc8259) specifies: > An implementation may set limits on the range and precision of numbers. When the default type is used, the maximal integer number that can be @@ -17703,7 +17703,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec will be automatically be stored as @ref number_unsigned_t or @ref number_float_t. - [RFC 7159](http://rfc7159.net/rfc7159) further states: + [RFC 8259](https://tools.ietf.org/html/rfc8259) further states: > Note that when such software is used, numbers that are integers and are > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense > that implementations will agree exactly on their numeric values. @@ -17726,7 +17726,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /*! @brief a type for a number (unsigned) - [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: + [RFC 8259](https://tools.ietf.org/html/rfc8259) describes numbers as follows: > The representation of numbers is similar to that used in most > programming languages. A number is represented in base 10 using decimal > digits. It contains an integer component that may be prefixed with an @@ -17764,7 +17764,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec #### Limits - [RFC 7159](http://rfc7159.net/rfc7159) specifies: + [RFC 8259](https://tools.ietf.org/html/rfc8259) specifies: > An implementation may set limits on the range and precision of numbers. When the default type is used, the maximal integer number that can be @@ -17774,7 +17774,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec deserialization, too large or small integer numbers will be automatically be stored as @ref number_integer_t or @ref number_float_t. - [RFC 7159](http://rfc7159.net/rfc7159) further states: + [RFC 8259](https://tools.ietf.org/html/rfc8259) further states: > Note that when such software is used, numbers that are integers and are > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense > that implementations will agree exactly on their numeric values. @@ -17797,7 +17797,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /*! @brief a type for a number (floating-point) - [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: + [RFC 8259](https://tools.ietf.org/html/rfc8259) describes numbers as follows: > The representation of numbers is similar to that used in most > programming languages. A number is represented in base 10 using decimal > digits. It contains an integer component that may be prefixed with an @@ -17835,7 +17835,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec #### Limits - [RFC 7159](http://rfc7159.net/rfc7159) states: + [RFC 8259](https://tools.ietf.org/html/rfc8259) states: > This specification allows implementations to set limits on the range and > precision of numbers accepted. Since software that implements IEEE > 754-2008 binary64 (double precision) numbers is generally available and diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp index 247d9d5fe..b0cdb0b35 100644 --- a/test/src/unit-class_parser.cpp +++ b/test/src/unit-class_parser.cpp @@ -589,7 +589,7 @@ TEST_CASE("parser class") SECTION("edge cases") { - // From RFC7159, Section 6: + // From RFC8259, Section 6: // Note that when such software is used, numbers that are // integers and are in the range [-(2**53)+1, (2**53)-1] // are interoperable in the sense that implementations will @@ -603,7 +603,7 @@ TEST_CASE("parser class") SECTION("over the edge cases") // issue #178 - Integer conversion to unsigned (incorrect handling of 64 bit integers) { - // While RFC7159, Section 6 specifies a preference for support + // While RFC8259, Section 6 specifies a preference for support // for ranges in range of IEEE 754-2008 binary64 (double precision) // this does not accommodate 64 bit integers without loss of accuracy. // As 64 bit integers are now widely used in software, it is desirable @@ -888,7 +888,7 @@ TEST_CASE("parser class") SECTION("edge cases") { - // From RFC7159, Section 6: + // From RFC8259, Section 6: // Note that when such software is used, numbers that are // integers and are in the range [-(2**53)+1, (2**53)-1] // are interoperable in the sense that implementations will @@ -902,7 +902,7 @@ TEST_CASE("parser class") SECTION("over the edge cases") // issue #178 - Integer conversion to unsigned (incorrect handling of 64 bit integers) { - // While RFC7159, Section 6 specifies a preference for support + // While RFC8259, Section 6 specifies a preference for support // for ranges in range of IEEE 754-2008 binary64 (double precision) // this does not accommodate 64 bit integers without loss of accuracy. // As 64 bit integers are now widely used in software, it is desirable diff --git a/test/src/unit-testsuites.cpp b/test/src/unit-testsuites.cpp index 96239987d..8071d4c74 100644 --- a/test/src/unit-testsuites.cpp +++ b/test/src/unit-testsuites.cpp @@ -418,9 +418,9 @@ TEST_CASE("json.org examples") } } -TEST_CASE("RFC 7159 examples") +TEST_CASE("RFC 8259 examples") { - // here, we list all JSON values from the RFC 7159 document + // here, we list all JSON values from the RFC 8259 document SECTION("7. Strings") { From d0ab2b86c34368f8f470c807041cefa6e2ff7a03 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Thu, 29 Apr 2021 17:19:57 +0200 Subject: [PATCH 31/32] :memo: update documentation --- doc/mkdocs/docs/features/macros.md | 4 ++++ include/nlohmann/detail/macro_unscope.hpp | 2 ++ single_include/nlohmann/json.hpp | 2 ++ 3 files changed, 8 insertions(+) diff --git a/doc/mkdocs/docs/features/macros.md b/doc/mkdocs/docs/features/macros.md index b468c091a..ee401c5dc 100644 --- a/doc/mkdocs/docs/features/macros.md +++ b/doc/mkdocs/docs/features/macros.md @@ -20,6 +20,10 @@ When enabled, exception messages contain a [JSON Pointer](json_pointer.md) to th The diagnostics messages can also be controlled with the CMake option `JSON_Diagnostics` (`OFF` by default) which sets `JSON_DIAGNOSTICS` accordingly. +## `JSON_HAS_CPP_11`, `JSON_HAS_CPP_14`, `JSON_HAS_CPP_17`, `JSON_HAS_CPP_20` + +The library targets C++11, but also supports some features introduced in later C++ versions (e.g., `std::string_view` support for C++17). For these new features, the library implements some preprocessor checks to determine the C++ standard. By defining any of these symbols, the internal check is overridden and the provided C++ version is unconditionally assumed. This can be helpful for compilers that only implement parts of the standard and would be detected incorrectly. + ## `JSON_NOEXCEPTION` Exceptions can be switched off by defining the symbol `JSON_NOEXCEPTION`. diff --git a/include/nlohmann/detail/macro_unscope.hpp b/include/nlohmann/detail/macro_unscope.hpp index 21c091daf..28be047aa 100644 --- a/include/nlohmann/detail/macro_unscope.hpp +++ b/include/nlohmann/detail/macro_unscope.hpp @@ -12,8 +12,10 @@ #undef JSON_THROW #undef JSON_TRY #undef JSON_PRIVATE_UNLESS_TESTED +#undef JSON_HAS_CPP_11 #undef JSON_HAS_CPP_14 #undef JSON_HAS_CPP_17 +#undef JSON_HAS_CPP_20 #undef NLOHMANN_BASIC_JSON_TPL_DECLARATION #undef NLOHMANN_BASIC_JSON_TPL #undef JSON_EXPLICIT diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 7f16e7743..b3c568b1a 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -25972,8 +25972,10 @@ inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std #undef JSON_THROW #undef JSON_TRY #undef JSON_PRIVATE_UNLESS_TESTED +#undef JSON_HAS_CPP_11 #undef JSON_HAS_CPP_14 #undef JSON_HAS_CPP_17 +#undef JSON_HAS_CPP_20 #undef NLOHMANN_BASIC_JSON_TPL_DECLARATION #undef NLOHMANN_BASIC_JSON_TPL #undef JSON_EXPLICIT From 193bf9d6b698d93e10bd37f6f82757fe3a4348c2 Mon Sep 17 00:00:00 2001 From: Morten Fyhn Amundsen Date: Mon, 3 May 2021 09:46:00 +0200 Subject: [PATCH 32/32] Fix typo in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bb931803c..3309190e0 100644 --- a/README.md +++ b/README.md @@ -142,7 +142,7 @@ target_link_libraries(foo PRIVATE nlohmann_json::nlohmann_json) Since CMake v3.11, [FetchContent](https://cmake.org/cmake/help/v3.11/module/FetchContent.html) can -be used to automatically download the repository as a dependency at configure type. +be used to automatically download the repository as a dependency at configure time. Example: ```cmake