From 727911bc80eb9656273a824e2521e1efa69fade9 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 23 Oct 2026 10:22:38 +0200 Subject: [PATCH] 2.4.5/2.4.6: from_json: share get_number_value and reserve_if_possible (#5891) The three number cases of get_arithmetic_value and the arithmetic from_json share one helper (boolean stays accepted only by the latter); the array and object reserve overload pairs become one. Signed-off-by: Niels Lohmann --- .../nlohmann/detail/conversions/from_json.hpp | 107 +++++++----------- 1 file changed, 41 insertions(+), 66 deletions(-) diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index 5cb123c59..1b127a2ef 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -69,12 +69,10 @@ void from_json(const BasicJsonType& j, std::optional& opt) } #endif // JSON_HAS_CPP_17 -// overloads for basic_json template parameters -template < typename BasicJsonType, typename ArithmeticType, - enable_if_t < std::is_arithmetic::value&& - !std::is_same::value, - int > = 0 > -void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) +// read a number value of any of the three number types into val; +// returns false (leaving val untouched) if j is not a number +template +bool get_number_value(const BasicJsonType& j, ArithmeticType& val) { switch (static_cast(j)) { @@ -85,17 +83,17 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) const auto* ptr = j.template get_ptr(); JSON_ASSERT(ptr != nullptr); val = static_cast(*ptr); - break; + return true; } case value_t::number_integer: { val = static_cast(*j.template get_ptr()); - break; + return true; } case value_t::number_float: { val = static_cast(*j.template get_ptr()); - break; + return true; } case value_t::null: @@ -106,7 +104,20 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) case value_t::binary: case value_t::discarded: default: - throw_type_must_be("number", j); + return false; + } +} + +// overloads for basic_json template parameters +template < typename BasicJsonType, typename ArithmeticType, + enable_if_t < std::is_arithmetic::value&& + !std::is_same::value, + int > = 0 > +void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) +{ + if (JSON_HEDLEY_UNLIKELY(!get_number_value(j, val))) + { + throw_type_must_be("number", j); } } @@ -256,18 +267,18 @@ auto from_json_array_impl(const BasicJsonType& j, std::array& arr, } } -// reserve() is called through this pair (modeled on from_json_object_reserve) -// so from_json_array_impl below has a single body for both ConstructibleArrayType -// that support reserve() and those that don't. -template -auto from_json_array_reserve(ConstructibleArrayType& arr, typename ConstructibleArrayType::size_type size, priority_tag<1> /*unused*/) --> decltype(arr.reserve(size), void()) +// reserve() is called through this pair (used for arrays and objects alike) +// so from_json_array_impl and the object from_json have a single body for both +// containers that support reserve() and those that don't. +template +auto reserve_if_possible(ConstructibleContainerType& c, typename ConstructibleContainerType::size_type size, priority_tag<1> /*unused*/) +-> decltype(c.reserve(size), void()) { - arr.reserve(size); + c.reserve(size); } -template -void from_json_array_reserve(ConstructibleArrayType& /*arr*/, std::size_t /*size*/, priority_tag<0> /*unused*/) +template +void reserve_if_possible(ConstructibleContainerType& /*c*/, std::size_t /*size*/, priority_tag<0> /*unused*/) {} // append through push_back() where available; containers without it (such as @@ -297,7 +308,7 @@ auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, p { ConstructibleArrayType ret; const auto* inner_array = j.template get_ptr(); - from_json_array_reserve(ret, inner_array->size(), priority_tag<1> {}); + reserve_if_possible(ret, inner_array->size(), priority_tag<1> {}); for (const auto& elem : *inner_array) { // get() returns *this, this won't call a from_json @@ -378,17 +389,6 @@ inline void from_json(const BasicJsonType& j, CompatibleArrayType& bin) } } -template -auto from_json_object_reserve(ConstructibleObjectType& obj, typename ConstructibleObjectType::size_type size, priority_tag<1> /*unused*/) --> decltype(obj.reserve(size), void()) -{ - obj.reserve(size); -} - -template -inline void from_json_object_reserve(ConstructibleObjectType& /*obj*/, std::size_t /*size*/, priority_tag<0> /*unused*/) -{} - // the keys of the source are sorted for the usual object types, so hinting the // end makes every insertion constant-time (for containers that order their keys // the same way); containers without emplace_hint() (such as ordered_map) fall @@ -417,7 +417,7 @@ inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) ConstructibleObjectType ret; const auto* inner_object = j.template get_ptr(); - from_json_object_reserve(ret, inner_object->size(), priority_tag<1> {}); + reserve_if_possible(ret, inner_object->size(), priority_tag<1> {}); for (const auto& p : *inner_object) { from_json_object_emplace(ret, p.first, p.second.template get(), priority_tag<1> {}); @@ -437,42 +437,17 @@ template < typename BasicJsonType, typename ArithmeticType, int > = 0 > inline void from_json(const BasicJsonType& j, ArithmeticType& val) { - switch (static_cast(j)) + if (get_number_value(j, val)) { - case value_t::number_unsigned: - { - // never null: a basic_json whose number_unsigned_t is number_integer_t - // stores no number_unsigned values (#2573) - const auto* ptr = j.template get_ptr(); - JSON_ASSERT(ptr != nullptr); - val = static_cast(*ptr); - break; - } - case value_t::number_integer: - { - val = static_cast(*j.template get_ptr()); - break; - } - case value_t::number_float: - { - val = static_cast(*j.template get_ptr()); - break; - } - case value_t::boolean: - { - val = static_cast(*j.template get_ptr()); - break; - } - - case value_t::null: - case value_t::object: - case value_t::array: - case value_t::string: - case value_t::binary: - case value_t::discarded: - default: - throw_type_must_be("number", j); + return; } + + // unlike get_arithmetic_value(), a boolean is accepted here + if (JSON_HEDLEY_UNLIKELY(!j.is_boolean())) + { + throw_type_must_be("number", j); + } + val = static_cast(*j.template get_ptr()); } template