diff --git a/include/nlohmann/adl_serializer.hpp b/include/nlohmann/adl_serializer.hpp index 4af1c4bb1d..f967612db2 100644 --- a/include/nlohmann/adl_serializer.hpp +++ b/include/nlohmann/adl_serializer.hpp @@ -1,14 +1,17 @@ #pragma once +#include #include #include #include +#include +#include namespace nlohmann { -template +template struct adl_serializer { /*! @@ -17,17 +20,39 @@ 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 default-constructible value types. + @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, 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); } + /*! + @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 are not default-constructible. + + @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::identity_tag {}))) + -> decltype(::nlohmann::from_json(std::forward(j), detail::identity_tag {})) + { + return ::nlohmann::from_json(std::forward(j), detail::identity_tag {}); + } + /*! @brief convert any value type to a JSON value @@ -37,13 +62,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, 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 6f8bd647b7..4e4efd0a66 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 @@ -187,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()), @@ -208,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*/) { @@ -247,6 +254,25 @@ void()) from_json_array_impl(j, arr, priority_tag<3> {}); } +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(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())) + { + 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 {}); +} + template void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin) { @@ -322,22 +348,47 @@ void from_json(const BasicJsonType& j, ArithmeticType& val) } } +template +std::tuple from_json_tuple_impl_base(BasicJsonType&& j, index_sequence /*unused*/) +{ + return std::make_tuple(std::forward(j).at(Idx).template get()...); +} + +template < typename BasicJsonType, class A1, class A2 > +std::pair from_json_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(const BasicJsonType& j, std::pair& p) +void from_json_tuple_impl(BasicJsonType&& j, std::pair& p, priority_tag<1> /*unused*/) { - p = {j.at(0).template get(), j.at(1).template get()}; + p = from_json_tuple_impl(std::forward(j), identity_tag> {}, priority_tag<0> {}); } -template -void from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence /*unused*/) +template +std::tuple from_json_tuple_impl(BasicJsonType&& j, identity_tag> /*unused*/, priority_tag<2> /*unused*/) { - t = std::make_tuple(j.at(Idx).template get::type>()...); + return from_json_tuple_impl_base(std::forward(j), index_sequence_for {}); } template -void from_json(const BasicJsonType& j, std::tuple& t) +void from_json_tuple_impl(BasicJsonType&& j, std::tuple& t, priority_tag<3> /*unused*/) { - from_json_tuple_impl(j, t, index_sequence_for {}); + 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<3> {})) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + 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> {}); } template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator, @@ -383,11 +434,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); + return from_json(j, std::forward(val)); } }; } // namespace detail diff --git a/include/nlohmann/detail/meta/identity_tag.hpp b/include/nlohmann/detail/meta/identity_tag.hpp new file mode 100644 index 0000000000..73a3e91700 --- /dev/null +++ b/include/nlohmann/detail/meta/identity_tag.hpp @@ -0,0 +1,10 @@ +#pragma once + +namespace nlohmann +{ +namespace detail +{ +// dispatching helper struct +template struct identity_tag {}; +} // namespace detail +} // namespace nlohmann diff --git a/include/nlohmann/detail/meta/type_traits.hpp b/include/nlohmann/detail/meta/type_traits.hpp index 1706cbdc6a..22d0bfe044 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; @@ -151,6 +150,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. +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 {}; @@ -193,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; }; @@ -216,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, @@ -247,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 @@ -265,7 +310,7 @@ struct is_constructible_string_type_impl < value_type_t, ConstructibleStringType>::value >> { static constexpr auto value = - std::is_constructible::value; }; @@ -288,7 +333,7 @@ struct is_compatible_array_type_impl < iterator_traits>::value >> { static constexpr bool value = - std::is_constructible::value; }; @@ -311,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&& @@ -355,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; @@ -382,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 diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 06ff303294..337b64c9d8 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -2920,50 +2920,53 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @{ /*! - @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 @a 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) @@ -2987,7 +2990,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec - @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 @@ -3003,23 +3005,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec @since version 2.1.0 */ - template < typename ValueTypeCV, typename ValueType = detail::uncvref_t, + template < typename ValueType, 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; @@ -3036,7 +3029,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec The function is equivalent to executing @code {.cpp} - return JSONSerializer::from_json(*this); + return JSONSerializer::from_json(*this); @endcode This overloads is chosen if: @@ -3047,7 +3040,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec @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 @@ -3056,134 +3048,116 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec @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()))) + template < typename ValueType, + detail::enable_if_t < + detail::has_non_default_from_json::value, + int > = 0 > + ValueType get_impl(detail::priority_tag<1> /*unused*/) const noexcept(noexcept( + JSONSerializer::from_json(std::declval()))) { - static_assert(!std::is_reference::value, - "get() cannot be used with reference types, you might want to use get_ref()"); return JSONSerializer::from_json(*this); } /*! - @brief get a value (explicit) - - Explicit type conversion between the JSON value and a compatible value. - The value is filled into the input parameter by calling the @ref json_serializer - `from_json()` method. - - The function is equivalent to executing - @code {.cpp} - ValueType v; - JSONSerializer::from_json(*this, v); - @endcode - - This overloads is chosen if: - - @a ValueType is not @ref basic_json, - - @ref json_serializer has a `from_json()` method of the form - `void from_json(const basic_json&, ValueType&)`, and + @brief get special-case overload - @tparam ValueType the input parameter type. + This overloads converts the current @ref basic_json in a different + @ref basic_json type - @return the input parameter, allowing chaining calls. + @tparam BasicJsonType == @ref basic_json - @throw what @ref json_serializer `from_json()` method throws + @return a copy of *this, converted into @a BasicJsonType - @liveexample{The example below shows several conversions from JSON values - to other types. There a few things to note: (1) Floating-point numbers can - be converted to integers\, (2) A JSON array can be converted to a standard - `std::vector`\, (3) A JSON object can be converted to C++ - associative containers such as `std::unordered_map`.,get_to} + @complexity Depending on the implementation of the called `from_json()` + method. - @since version 3.3.0 + @since version 3.2.0 */ - template < typename ValueType, + template < typename BasicJsonType, detail::enable_if_t < - !detail::is_basic_json::value&& - detail::has_from_json::value, + detail::is_basic_json::value, int > = 0 > - ValueType & get_to(ValueType& v) const noexcept(noexcept( - JSONSerializer::from_json(std::declval(), v))) + BasicJsonType get_impl(detail::priority_tag<2> /*unused*/) const { - JSONSerializer::from_json(*this, v); - return v; + return *this; } - // specialization to allow to call get_to with a basic_json value - // see https://github.com/nlohmann/json/issues/2175 - template::value, + /*! + @brief get special-case overload + + This overloads avoids a lot of template boilerplate, it can be seen as the + identity method + + @tparam BasicJsonType == @ref basic_json + + @return a copy of *this + + @complexity Constant. + + @since version 2.1.0 + */ + template::value, int> = 0> - ValueType & get_to(ValueType& v) const + basic_json get_impl(detail::priority_tag<3> /*unused*/) const { - v = *this; - return v; + return *this; } - template < - typename T, std::size_t N, - typename Array = T (&)[N], // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - detail::enable_if_t < - detail::has_from_json::value, int > = 0 > - Array get_to(T (&v)[N]) const // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - noexcept(noexcept(JSONSerializer::from_json( - std::declval(), v))) + /*! + @brief get a pointer value (explicit) + @copydoc get() + */ + template::value, + int> = 0> + constexpr auto get_impl(detail::priority_tag<4> /*unused*/) const noexcept + -> decltype(std::declval().template get_ptr()) { - JSONSerializer::from_json(*this, v); - return v; + // delegate the call to get_ptr + return get_ptr(); } - + public: /*! - @brief get a pointer value (implicit) + @brief get a (pointer) value (explicit) - Implicit pointer access to the internally stored JSON value. No copies are - made. + Performs explicit type conversion between the JSON value and a compatible value if required. - @warning Writing data to the pointee of the result yields an undefined - state. + - If the requested type is a pointer to the internally stored JSON value that pointer is returned. + No copies are made. - @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref - object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, - @ref number_unsigned_t, or @ref number_float_t. Enforced by a static - assertion. + - If the requested type is the current @ref basic_json, or a different @ref basic_json convertible + from the current @ref basic_json. - @return pointer to the internally stored JSON value if the requested - pointer type @a PointerType fits to the JSON value; `nullptr` otherwise + - Otherwise the value is converted by calling the @ref json_serializer `from_json()` + method. - @complexity Constant. + @tparam ValueTypeCV the provided value type + @tparam ValueType the returned value type - @liveexample{The example below shows how pointers to internal values of a - JSON value can be requested. Note that no type conversions are made and a - `nullptr` is returned if the value and the requested pointer type does not - match.,get_ptr} + @return copy of the JSON value, converted to @tparam ValueType if necessary - @since version 1.0.0 - */ - template::value, int>::type = 0> - auto get_ptr() noexcept -> decltype(std::declval().get_impl_ptr(std::declval())) - { - // delegate the call to get_impl_ptr<>() - return get_impl_ptr(static_cast(nullptr)); - } + @throw what @ref json_serializer `from_json()` method throws if conversion is required - /*! - @brief get a pointer value (implicit) - @copydoc get_ptr() + @since version 2.1.0 */ - template < typename PointerType, typename std::enable_if < - std::is_pointer::value&& - std::is_const::type>::value, int >::type = 0 > - constexpr auto get_ptr() const noexcept -> decltype(std::declval().get_impl_ptr(std::declval())) + template < typename ValueTypeCV, typename ValueType = detail::uncvref_t> +#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> {})) { - // delegate the call to get_impl_ptr<>() const - return get_impl_ptr(static_cast(nullptr)); + // we cannot static_assert on ValueTypeCV being non-const, because + // there is support for get(), which is why we + // still need the uncvref + static_assert(!std::is_reference::value, + "get() cannot be used with reference types, you might want to use get_ref()"); + return get_impl(detail::priority_tag<4> {}); } /*! @@ -3222,15 +3196,73 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec } /*! - @brief get a pointer value (explicit) - @copydoc get() + @brief get a value (explicit) + + Explicit type conversion between the JSON value and a compatible value. + The value is filled into the input parameter by calling the @ref json_serializer + `from_json()` method. + + The function is equivalent to executing + @code {.cpp} + ValueType v; + JSONSerializer::from_json(*this, v); + @endcode + + This overloads is chosen if: + - @a ValueType is not @ref basic_json, + - @ref json_serializer has a `from_json()` method of the form + `void from_json(const basic_json&, ValueType&)`, and + + @tparam ValueType the input parameter type. + + @return the input parameter, allowing chaining calls. + + @throw what @ref json_serializer `from_json()` method throws + + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,get_to} + + @since version 3.3.0 */ - template::value, int>::type = 0> - constexpr auto get() const noexcept -> decltype(std::declval().template get_ptr()) + template < typename ValueType, + detail::enable_if_t < + !detail::is_basic_json::value&& + detail::has_from_json::value, + int > = 0 > + ValueType & get_to(ValueType& v) const noexcept(noexcept( + JSONSerializer::from_json(std::declval(), v))) { - // delegate the call to get_ptr - return get_ptr(); + JSONSerializer::from_json(*this, v); + return v; + } + + // specialization to allow to call get_to with a basic_json value + // see https://github.com/nlohmann/json/issues/2175 + template::value, + int> = 0> + ValueType & get_to(ValueType& v) const + { + v = *this; + return v; + } + + template < + typename T, std::size_t N, + typename Array = T (&)[N], // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + detail::enable_if_t < + detail::has_from_json::value, int > = 0 > + Array get_to(T (&v)[N]) const // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + noexcept(noexcept(JSONSerializer::from_json( + std::declval(), v))) + { + JSONSerializer::from_json(*this, v); + return v; } /*! diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 7ef4befb29..97f77b4faa 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -49,6 +49,7 @@ SOFTWARE. // #include +#include #include // #include @@ -3166,6 +3167,18 @@ constexpr T static_const::value; } // namespace detail } // namespace nlohmann +// #include + + +namespace nlohmann +{ +namespace detail +{ +// dispatching helper struct +template struct identity_tag {}; +} // namespace detail +} // namespace nlohmann + // #include @@ -3483,8 +3496,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; @@ -3528,6 +3540,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. +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 {}; @@ -3570,9 +3628,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; }; @@ -3593,10 +3651,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, @@ -3624,7 +3682,7 @@ struct is_compatible_string_type_impl < value_type_t, CompatibleStringType>::value >> { static constexpr auto value = - std::is_constructible::value; + is_constructible::value; }; template @@ -3642,7 +3700,7 @@ struct is_constructible_string_type_impl < value_type_t, ConstructibleStringType>::value >> { static constexpr auto value = - std::is_constructible::value; }; @@ -3665,7 +3723,7 @@ struct is_compatible_array_type_impl < iterator_traits>::value >> { static constexpr bool value = - std::is_constructible::value; }; @@ -3688,7 +3746,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&& @@ -3732,7 +3790,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; @@ -3759,18 +3817,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 @@ -3946,7 +3997,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()), @@ -3967,7 +4021,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*/) { @@ -4006,6 +4063,25 @@ void()) from_json_array_impl(j, arr, priority_tag<3> {}); } +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(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())) + { + 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 {}); +} + template void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin) { @@ -4081,22 +4157,47 @@ void from_json(const BasicJsonType& j, ArithmeticType& val) } } +template +std::tuple from_json_tuple_impl_base(BasicJsonType&& j, index_sequence /*unused*/) +{ + return std::make_tuple(std::forward(j).at(Idx).template get()...); +} + +template < typename BasicJsonType, class A1, class A2 > +std::pair from_json_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(const BasicJsonType& j, std::pair& p) +void from_json_tuple_impl(BasicJsonType&& j, std::pair& p, priority_tag<1> /*unused*/) { - p = {j.at(0).template get(), j.at(1).template get()}; + p = from_json_tuple_impl(std::forward(j), identity_tag> {}, priority_tag<0> {}); } -template -void from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence /*unused*/) +template +std::tuple from_json_tuple_impl(BasicJsonType&& j, identity_tag> /*unused*/, priority_tag<2> /*unused*/) { - t = std::make_tuple(j.at(Idx).template get::type>()...); + return from_json_tuple_impl_base(std::forward(j), index_sequence_for {}); } template -void from_json(const BasicJsonType& j, std::tuple& t) +void from_json_tuple_impl(BasicJsonType&& j, std::tuple& t, priority_tag<3> /*unused*/) { - from_json_tuple_impl(j, t, index_sequence_for {}); + 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<3> {})) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + 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> {}); } template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator, @@ -4142,11 +4243,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); + return from_json(j, std::forward(val)); } }; } // namespace detail @@ -4731,11 +4832,15 @@ constexpr const auto& to_json = detail::static_const::value; } // namespace } // namespace nlohmann +// #include + +// #include + namespace nlohmann { -template +template struct adl_serializer { /*! @@ -4744,17 +4849,39 @@ 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 default-constructible value types. + @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, 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); } + /*! + @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 are not default-constructible. + + @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::identity_tag {}))) + -> decltype(::nlohmann::from_json(std::forward(j), detail::identity_tag {})) + { + return ::nlohmann::from_json(std::forward(j), detail::identity_tag {}); + } + /*! @brief convert any value type to a JSON value @@ -4764,15 +4891,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, 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 // #include @@ -19809,50 +19935,53 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @{ /*! - @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 @a 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) @@ -19876,7 +20005,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec - @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 @@ -19892,23 +20020,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec @since version 2.1.0 */ - template < typename ValueTypeCV, typename ValueType = detail::uncvref_t, + template < typename ValueType, 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; @@ -19925,7 +20044,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec The function is equivalent to executing @code {.cpp} - return JSONSerializer::from_json(*this); + return JSONSerializer::from_json(*this); @endcode This overloads is chosen if: @@ -19936,7 +20055,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec @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 @@ -19945,134 +20063,116 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec @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()))) + template < typename ValueType, + detail::enable_if_t < + detail::has_non_default_from_json::value, + int > = 0 > + ValueType get_impl(detail::priority_tag<1> /*unused*/) const noexcept(noexcept( + JSONSerializer::from_json(std::declval()))) { - static_assert(!std::is_reference::value, - "get() cannot be used with reference types, you might want to use get_ref()"); return JSONSerializer::from_json(*this); } /*! - @brief get a value (explicit) - - Explicit type conversion between the JSON value and a compatible value. - The value is filled into the input parameter by calling the @ref json_serializer - `from_json()` method. - - The function is equivalent to executing - @code {.cpp} - ValueType v; - JSONSerializer::from_json(*this, v); - @endcode - - This overloads is chosen if: - - @a ValueType is not @ref basic_json, - - @ref json_serializer has a `from_json()` method of the form - `void from_json(const basic_json&, ValueType&)`, and + @brief get special-case overload - @tparam ValueType the input parameter type. + This overloads converts the current @ref basic_json in a different + @ref basic_json type - @return the input parameter, allowing chaining calls. + @tparam BasicJsonType == @ref basic_json - @throw what @ref json_serializer `from_json()` method throws + @return a copy of *this, converted into @a BasicJsonType - @liveexample{The example below shows several conversions from JSON values - to other types. There a few things to note: (1) Floating-point numbers can - be converted to integers\, (2) A JSON array can be converted to a standard - `std::vector`\, (3) A JSON object can be converted to C++ - associative containers such as `std::unordered_map`.,get_to} + @complexity Depending on the implementation of the called `from_json()` + method. - @since version 3.3.0 + @since version 3.2.0 */ - template < typename ValueType, + template < typename BasicJsonType, detail::enable_if_t < - !detail::is_basic_json::value&& - detail::has_from_json::value, + detail::is_basic_json::value, int > = 0 > - ValueType & get_to(ValueType& v) const noexcept(noexcept( - JSONSerializer::from_json(std::declval(), v))) + BasicJsonType get_impl(detail::priority_tag<2> /*unused*/) const { - JSONSerializer::from_json(*this, v); - return v; + return *this; } - // specialization to allow to call get_to with a basic_json value - // see https://github.com/nlohmann/json/issues/2175 - template::value, + /*! + @brief get special-case overload + + This overloads avoids a lot of template boilerplate, it can be seen as the + identity method + + @tparam BasicJsonType == @ref basic_json + + @return a copy of *this + + @complexity Constant. + + @since version 2.1.0 + */ + template::value, int> = 0> - ValueType & get_to(ValueType& v) const + basic_json get_impl(detail::priority_tag<3> /*unused*/) const { - v = *this; - return v; + return *this; } - template < - typename T, std::size_t N, - typename Array = T (&)[N], // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - detail::enable_if_t < - detail::has_from_json::value, int > = 0 > - Array get_to(T (&v)[N]) const // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - noexcept(noexcept(JSONSerializer::from_json( - std::declval(), v))) + /*! + @brief get a pointer value (explicit) + @copydoc get() + */ + template::value, + int> = 0> + constexpr auto get_impl(detail::priority_tag<4> /*unused*/) const noexcept + -> decltype(std::declval().template get_ptr()) { - JSONSerializer::from_json(*this, v); - return v; + // delegate the call to get_ptr + return get_ptr(); } - + public: /*! - @brief get a pointer value (implicit) + @brief get a (pointer) value (explicit) - Implicit pointer access to the internally stored JSON value. No copies are - made. + Performs explicit type conversion between the JSON value and a compatible value if required. - @warning Writing data to the pointee of the result yields an undefined - state. + - If the requested type is a pointer to the internally stored JSON value that pointer is returned. + No copies are made. - @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref - object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, - @ref number_unsigned_t, or @ref number_float_t. Enforced by a static - assertion. + - If the requested type is the current @ref basic_json, or a different @ref basic_json convertible + from the current @ref basic_json. - @return pointer to the internally stored JSON value if the requested - pointer type @a PointerType fits to the JSON value; `nullptr` otherwise + - Otherwise the value is converted by calling the @ref json_serializer `from_json()` + method. - @complexity Constant. + @tparam ValueTypeCV the provided value type + @tparam ValueType the returned value type - @liveexample{The example below shows how pointers to internal values of a - JSON value can be requested. Note that no type conversions are made and a - `nullptr` is returned if the value and the requested pointer type does not - match.,get_ptr} + @return copy of the JSON value, converted to @tparam ValueType if necessary - @since version 1.0.0 - */ - template::value, int>::type = 0> - auto get_ptr() noexcept -> decltype(std::declval().get_impl_ptr(std::declval())) - { - // delegate the call to get_impl_ptr<>() - return get_impl_ptr(static_cast(nullptr)); - } + @throw what @ref json_serializer `from_json()` method throws if conversion is required - /*! - @brief get a pointer value (implicit) - @copydoc get_ptr() + @since version 2.1.0 */ - template < typename PointerType, typename std::enable_if < - std::is_pointer::value&& - std::is_const::type>::value, int >::type = 0 > - constexpr auto get_ptr() const noexcept -> decltype(std::declval().get_impl_ptr(std::declval())) + template < typename ValueTypeCV, typename ValueType = detail::uncvref_t> +#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> {})) { - // delegate the call to get_impl_ptr<>() const - return get_impl_ptr(static_cast(nullptr)); + // we cannot static_assert on ValueTypeCV being non-const, because + // there is support for get(), which is why we + // still need the uncvref + static_assert(!std::is_reference::value, + "get() cannot be used with reference types, you might want to use get_ref()"); + return get_impl(detail::priority_tag<4> {}); } /*! @@ -20111,15 +20211,73 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec } /*! - @brief get a pointer value (explicit) - @copydoc get() + @brief get a value (explicit) + + Explicit type conversion between the JSON value and a compatible value. + The value is filled into the input parameter by calling the @ref json_serializer + `from_json()` method. + + The function is equivalent to executing + @code {.cpp} + ValueType v; + JSONSerializer::from_json(*this, v); + @endcode + + This overloads is chosen if: + - @a ValueType is not @ref basic_json, + - @ref json_serializer has a `from_json()` method of the form + `void from_json(const basic_json&, ValueType&)`, and + + @tparam ValueType the input parameter type. + + @return the input parameter, allowing chaining calls. + + @throw what @ref json_serializer `from_json()` method throws + + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,get_to} + + @since version 3.3.0 */ - template::value, int>::type = 0> - constexpr auto get() const noexcept -> decltype(std::declval().template get_ptr()) + template < typename ValueType, + detail::enable_if_t < + !detail::is_basic_json::value&& + detail::has_from_json::value, + int > = 0 > + ValueType & get_to(ValueType& v) const noexcept(noexcept( + JSONSerializer::from_json(std::declval(), v))) { - // delegate the call to get_ptr - return get_ptr(); + JSONSerializer::from_json(*this, v); + return v; + } + + // specialization to allow to call get_to with a basic_json value + // see https://github.com/nlohmann/json/issues/2175 + template::value, + int> = 0> + ValueType & get_to(ValueType& v) const + { + v = *this; + return v; + } + + template < + typename T, std::size_t N, + typename Array = T (&)[N], // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + detail::enable_if_t < + detail::has_from_json::value, int > = 0 > + Array get_to(T (&v)[N]) const // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + noexcept(noexcept(JSONSerializer::from_json( + std::declval(), v))) + { + JSONSerializer::from_json(*this, v); + return v; } /*! diff --git a/test/src/unit-regression2.cpp b/test/src/unit-regression2.cpp index 6260176775..4d48e4765b 100644 --- a/test/src/unit-regression2.cpp +++ b/test/src/unit-regression2.cpp @@ -134,6 +134,29 @@ struct NotSerializableData }; +///////////////////////////////////////////////////////////////////// +// for #2574 +///////////////////////////////////////////////////////////////////// + +struct NonDefaultConstructible +{ + explicit NonDefaultConstructible (int a) : x(a) { } + int x; +}; + +namespace nlohmann +{ +template <> +struct adl_serializer +{ + static NonDefaultConstructible from_json (json const& j) + { + return NonDefaultConstructible(j.get()); + } +}; +} // namespace nlohmann + + TEST_CASE("regression tests 2") { SECTION("issue #1001 - Fix memory leak during parser callback") @@ -501,4 +524,74 @@ 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); + + } + + { + json j = 7; + CHECK_THROWS_AS((j.get>()), json::type_error); + } + } + + SECTION("std::pair") + { + { + json j = { 3, 8 }; + 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); + } + + { + json j = 7; + CHECK_THROWS_AS((j.get>()), json::type_error); + } + } + + 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); + } + + { + json j = 7; + CHECK_THROWS_AS((j.get>()), json::type_error); + } + } + } }