Unverified Commit a34e011e authored by Niels Lohmann's avatar Niels Lohmann Committed by GitHub

Merge pull request #2576 from AnthonyVH/non_default_constructable_stl_containers

Add support for deserialization of STL containers of non-default constructable types (fixes #2574).
parents f1600cd2 2b865131
#pragma once #pragma once
#include <type_traits>
#include <utility> #include <utility>
#include <nlohmann/detail/conversions/from_json.hpp> #include <nlohmann/detail/conversions/from_json.hpp>
#include <nlohmann/detail/conversions/to_json.hpp> #include <nlohmann/detail/conversions/to_json.hpp>
#include <nlohmann/detail/meta/identity_tag.hpp>
#include <nlohmann/detail/meta/type_traits.hpp>
namespace nlohmann namespace nlohmann
{ {
template<typename, typename> template<typename ValueType, typename>
struct adl_serializer struct adl_serializer
{ {
/*! /*!
...@@ -17,17 +20,39 @@ struct adl_serializer ...@@ -17,17 +20,39 @@ struct adl_serializer
This function is usually called by the `get()` function of the This function is usually called by the `get()` function of the
@ref basic_json class (either explicit or via conversion operators). @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] j JSON value to read from
@param[in,out] val value to write to @param[in,out] val value to write to
*/ */
template<typename BasicJsonType, typename ValueType> template<typename BasicJsonType, typename TargetType = ValueType>
static auto from_json(BasicJsonType&& j, ValueType& val) noexcept( static auto from_json(BasicJsonType && j, TargetType& val) noexcept(
noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), val))) noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), val)))
-> decltype(::nlohmann::from_json(std::forward<BasicJsonType>(j), val), void()) -> decltype(::nlohmann::from_json(std::forward<BasicJsonType>(j), val), void())
{ {
::nlohmann::from_json(std::forward<BasicJsonType>(j), val); ::nlohmann::from_json(std::forward<BasicJsonType>(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<typename BasicJsonType, typename TargetType = ValueType>
static auto from_json(BasicJsonType && j) noexcept(
noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {})))
-> decltype(::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {}))
{
return ::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {});
}
/*! /*!
@brief convert any value type to a JSON value @brief convert any value type to a JSON value
...@@ -37,13 +62,12 @@ struct adl_serializer ...@@ -37,13 +62,12 @@ struct adl_serializer
@param[in,out] j JSON value to write to @param[in,out] j JSON value to write to
@param[in] val value to read from @param[in] val value to read from
*/ */
template<typename BasicJsonType, typename ValueType> template<typename BasicJsonType, typename TargetType = ValueType>
static auto to_json(BasicJsonType& j, ValueType&& val) noexcept( static auto to_json(BasicJsonType& j, TargetType && val) noexcept(
noexcept(::nlohmann::to_json(j, std::forward<ValueType>(val)))) noexcept(::nlohmann::to_json(j, std::forward<TargetType>(val))))
-> decltype(::nlohmann::to_json(j, std::forward<ValueType>(val)), void()) -> decltype(::nlohmann::to_json(j, std::forward<TargetType>(val)), void())
{ {
::nlohmann::to_json(j, std::forward<ValueType>(val)); ::nlohmann::to_json(j, std::forward<TargetType>(val));
} }
}; };
} // namespace nlohmann } // namespace nlohmann
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <nlohmann/detail/exceptions.hpp> #include <nlohmann/detail/exceptions.hpp>
#include <nlohmann/detail/macro_scope.hpp> #include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/detail/meta/cpp_future.hpp> #include <nlohmann/detail/meta/cpp_future.hpp>
#include <nlohmann/detail/meta/identity_tag.hpp>
#include <nlohmann/detail/meta/type_traits.hpp> #include <nlohmann/detail/meta/type_traits.hpp>
#include <nlohmann/detail/value_t.hpp> #include <nlohmann/detail/value_t.hpp>
...@@ -187,7 +188,10 @@ auto from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr, ...@@ -187,7 +188,10 @@ auto from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr,
} }
} }
template<typename BasicJsonType, typename ConstructibleArrayType> template<typename BasicJsonType, typename ConstructibleArrayType,
enable_if_t<
std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value,
int> = 0>
auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/) auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/)
-> decltype( -> decltype(
arr.reserve(std::declval<typename ConstructibleArrayType::size_type>()), arr.reserve(std::declval<typename ConstructibleArrayType::size_type>()),
...@@ -208,7 +212,10 @@ auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, p ...@@ -208,7 +212,10 @@ auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, p
arr = std::move(ret); arr = std::move(ret);
} }
template<typename BasicJsonType, typename ConstructibleArrayType> template<typename BasicJsonType, typename ConstructibleArrayType,
enable_if_t<
std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value,
int> = 0>
void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr,
priority_tag<0> /*unused*/) priority_tag<0> /*unused*/)
{ {
...@@ -247,6 +254,25 @@ void()) ...@@ -247,6 +254,25 @@ void())
from_json_array_impl(j, arr, priority_tag<3> {}); from_json_array_impl(j, arr, priority_tag<3> {});
} }
template < typename BasicJsonType, typename T, std::size_t... Idx >
std::array<T, sizeof...(Idx)> from_json_inplace_array_impl(BasicJsonType&& j,
identity_tag<std::array<T, sizeof...(Idx)>> /*unused*/, index_sequence<Idx...> /*unused*/)
{
return { { std::forward<BasicJsonType>(j).at(Idx).template get<T>()... } };
}
template < typename BasicJsonType, typename T, std::size_t N >
auto from_json(BasicJsonType&& j, identity_tag<std::array<T, N>> tag)
-> decltype(from_json_inplace_array_impl(std::forward<BasicJsonType>(j), tag, make_index_sequence<N> {}))
{
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<BasicJsonType>(j), tag, make_index_sequence<N> {});
}
template<typename BasicJsonType> template<typename BasicJsonType>
void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin) void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin)
{ {
...@@ -322,22 +348,47 @@ void from_json(const BasicJsonType& j, ArithmeticType& val) ...@@ -322,22 +348,47 @@ void from_json(const BasicJsonType& j, ArithmeticType& val)
} }
} }
template<typename BasicJsonType, typename... Args, std::size_t... Idx>
std::tuple<Args...> from_json_tuple_impl_base(BasicJsonType&& j, index_sequence<Idx...> /*unused*/)
{
return std::make_tuple(std::forward<BasicJsonType>(j).at(Idx).template get<Args>()...);
}
template < typename BasicJsonType, class A1, class A2 >
std::pair<A1, A2> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::pair<A1, A2>> /*unused*/, priority_tag<0> /*unused*/)
{
return {std::forward<BasicJsonType>(j).at(0).template get<A1>(),
std::forward<BasicJsonType>(j).at(1).template get<A2>()};
}
template<typename BasicJsonType, typename A1, typename A2> template<typename BasicJsonType, typename A1, typename A2>
void from_json(const BasicJsonType& j, std::pair<A1, A2>& p) void from_json_tuple_impl(BasicJsonType&& j, std::pair<A1, A2>& p, priority_tag<1> /*unused*/)
{ {
p = {j.at(0).template get<A1>(), j.at(1).template get<A2>()}; p = from_json_tuple_impl(std::forward<BasicJsonType>(j), identity_tag<std::pair<A1, A2>> {}, priority_tag<0> {});
} }
template<typename BasicJsonType, typename Tuple, std::size_t... Idx> template<typename BasicJsonType, typename... Args>
void from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence<Idx...> /*unused*/) std::tuple<Args...> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::tuple<Args...>> /*unused*/, priority_tag<2> /*unused*/)
{ {
t = std::make_tuple(j.at(Idx).template get<typename std::tuple_element<Idx, Tuple>::type>()...); return from_json_tuple_impl_base<BasicJsonType, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});
} }
template<typename BasicJsonType, typename... Args> template<typename BasicJsonType, typename... Args>
void from_json(const BasicJsonType& j, std::tuple<Args...>& t) void from_json_tuple_impl(BasicJsonType&& j, std::tuple<Args...>& t, priority_tag<3> /*unused*/)
{ {
from_json_tuple_impl(j, t, index_sequence_for<Args...> {}); t = from_json_tuple_impl_base<BasicJsonType, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});
}
template<typename BasicJsonType, typename TupleRelated>
auto from_json(BasicJsonType&& j, TupleRelated&& t)
-> decltype(from_json_tuple_impl(std::forward<BasicJsonType>(j), std::forward<TupleRelated>(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<BasicJsonType>(j), std::forward<TupleRelated>(t), priority_tag<3> {});
} }
template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator, template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator,
...@@ -383,11 +434,11 @@ void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyE ...@@ -383,11 +434,11 @@ void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyE
struct from_json_fn struct from_json_fn
{ {
template<typename BasicJsonType, typename T> template<typename BasicJsonType, typename T>
auto operator()(const BasicJsonType& j, T& val) const auto operator()(const BasicJsonType& j, T&& val) const
noexcept(noexcept(from_json(j, val))) noexcept(noexcept(from_json(j, std::forward<T>(val))))
-> decltype(from_json(j, val), void()) -> decltype(from_json(j, std::forward<T>(val)))
{ {
return from_json(j, val); return from_json(j, std::forward<T>(val));
} }
}; };
} // namespace detail } // namespace detail
......
#pragma once
namespace nlohmann
{
namespace detail
{
// dispatching helper struct
template <class T> struct identity_tag {};
} // namespace detail
} // namespace nlohmann
...@@ -106,8 +106,7 @@ struct is_getable ...@@ -106,8 +106,7 @@ struct is_getable
}; };
template<typename BasicJsonType, typename T> template<typename BasicJsonType, typename T>
struct has_from_json < BasicJsonType, T, struct has_from_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>
enable_if_t < !is_basic_json<T>::value >>
{ {
using serializer = typename BasicJsonType::template json_serializer<T, void>; using serializer = typename BasicJsonType::template json_serializer<T, void>;
...@@ -151,6 +150,52 @@ struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >> ...@@ -151,6 +150,52 @@ struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>
// is_ functions // // is_ functions //
/////////////////// ///////////////////
// https://en.cppreference.com/w/cpp/types/conjunction
template<class...> struct conjunction : std::true_type { };
template<class B1> struct conjunction<B1> : B1 { };
template<class B1, class... Bn>
struct conjunction<B1, Bn...>
: std::conditional<bool(B1::value), conjunction<Bn...>, 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 <typename T>
struct is_default_constructible : std::is_default_constructible<T> {};
template <typename T1, typename T2>
struct is_default_constructible<std::pair<T1, T2>>
: conjunction<is_default_constructible<T1>, is_default_constructible<T2>> {};
template <typename T1, typename T2>
struct is_default_constructible<const std::pair<T1, T2>>
: conjunction<is_default_constructible<T1>, is_default_constructible<T2>> {};
template <typename... Ts>
struct is_default_constructible<std::tuple<Ts...>>
: conjunction<is_default_constructible<Ts>...> {};
template <typename... Ts>
struct is_default_constructible<const std::tuple<Ts...>>
: conjunction<is_default_constructible<Ts>...> {};
template <typename T, typename... Args>
struct is_constructible : std::is_constructible<T, Args...> {};
template <typename T1, typename T2>
struct is_constructible<std::pair<T1, T2>> : is_default_constructible<std::pair<T1, T2>> {};
template <typename T1, typename T2>
struct is_constructible<const std::pair<T1, T2>> : is_default_constructible<const std::pair<T1, T2>> {};
template <typename... Ts>
struct is_constructible<std::tuple<Ts...>> : is_default_constructible<std::tuple<Ts...>> {};
template <typename... Ts>
struct is_constructible<const std::tuple<Ts...>> : is_default_constructible<const std::tuple<Ts...>> {};
template<typename T, typename = void> template<typename T, typename = void>
struct is_iterator_traits : std::false_type {}; struct is_iterator_traits : std::false_type {};
...@@ -193,9 +238,9 @@ struct is_compatible_object_type_impl < ...@@ -193,9 +238,9 @@ struct is_compatible_object_type_impl <
// macOS's is_constructible does not play well with nonesuch... // macOS's is_constructible does not play well with nonesuch...
static constexpr bool value = static constexpr bool value =
std::is_constructible<typename object_t::key_type, is_constructible<typename object_t::key_type,
typename CompatibleObjectType::key_type>::value && typename CompatibleObjectType::key_type>::value &&
std::is_constructible<typename object_t::mapped_type, is_constructible<typename object_t::mapped_type,
typename CompatibleObjectType::mapped_type>::value; typename CompatibleObjectType::mapped_type>::value;
}; };
...@@ -216,10 +261,10 @@ struct is_constructible_object_type_impl < ...@@ -216,10 +261,10 @@ struct is_constructible_object_type_impl <
using object_t = typename BasicJsonType::object_t; using object_t = typename BasicJsonType::object_t;
static constexpr bool value = static constexpr bool value =
(std::is_default_constructible<ConstructibleObjectType>::value && (is_default_constructible<ConstructibleObjectType>::value &&
(std::is_move_assignable<ConstructibleObjectType>::value || (std::is_move_assignable<ConstructibleObjectType>::value ||
std::is_copy_assignable<ConstructibleObjectType>::value) && std::is_copy_assignable<ConstructibleObjectType>::value) &&
(std::is_constructible<typename ConstructibleObjectType::key_type, (is_constructible<typename ConstructibleObjectType::key_type,
typename object_t::key_type>::value && typename object_t::key_type>::value &&
std::is_same < std::is_same <
typename object_t::mapped_type, typename object_t::mapped_type,
...@@ -247,7 +292,7 @@ struct is_compatible_string_type_impl < ...@@ -247,7 +292,7 @@ struct is_compatible_string_type_impl <
value_type_t, CompatibleStringType>::value >> value_type_t, CompatibleStringType>::value >>
{ {
static constexpr auto value = static constexpr auto value =
std::is_constructible<typename BasicJsonType::string_t, CompatibleStringType>::value; is_constructible<typename BasicJsonType::string_t, CompatibleStringType>::value;
}; };
template<typename BasicJsonType, typename ConstructibleStringType> template<typename BasicJsonType, typename ConstructibleStringType>
...@@ -265,7 +310,7 @@ struct is_constructible_string_type_impl < ...@@ -265,7 +310,7 @@ struct is_constructible_string_type_impl <
value_type_t, ConstructibleStringType>::value >> value_type_t, ConstructibleStringType>::value >>
{ {
static constexpr auto value = static constexpr auto value =
std::is_constructible<ConstructibleStringType, is_constructible<ConstructibleStringType,
typename BasicJsonType::string_t>::value; typename BasicJsonType::string_t>::value;
}; };
...@@ -288,7 +333,7 @@ struct is_compatible_array_type_impl < ...@@ -288,7 +333,7 @@ struct is_compatible_array_type_impl <
iterator_traits<CompatibleArrayType >>::value >> iterator_traits<CompatibleArrayType >>::value >>
{ {
static constexpr bool value = static constexpr bool value =
std::is_constructible<BasicJsonType, is_constructible<BasicJsonType,
typename CompatibleArrayType::value_type>::value; typename CompatibleArrayType::value_type>::value;
}; };
...@@ -311,7 +356,7 @@ struct is_constructible_array_type_impl < ...@@ -311,7 +356,7 @@ struct is_constructible_array_type_impl <
BasicJsonType, ConstructibleArrayType, BasicJsonType, ConstructibleArrayType,
enable_if_t < !std::is_same<ConstructibleArrayType, enable_if_t < !std::is_same<ConstructibleArrayType,
typename BasicJsonType::value_type>::value&& typename BasicJsonType::value_type>::value&&
std::is_default_constructible<ConstructibleArrayType>::value&& is_default_constructible<ConstructibleArrayType>::value&&
(std::is_move_assignable<ConstructibleArrayType>::value || (std::is_move_assignable<ConstructibleArrayType>::value ||
std::is_copy_assignable<ConstructibleArrayType>::value)&& std::is_copy_assignable<ConstructibleArrayType>::value)&&
is_detected<value_type_t, ConstructibleArrayType>::value&& is_detected<value_type_t, ConstructibleArrayType>::value&&
...@@ -355,7 +400,7 @@ struct is_compatible_integer_type_impl < ...@@ -355,7 +400,7 @@ struct is_compatible_integer_type_impl <
using CompatibleLimits = std::numeric_limits<CompatibleNumberIntegerType>; using CompatibleLimits = std::numeric_limits<CompatibleNumberIntegerType>;
static constexpr auto value = static constexpr auto value =
std::is_constructible<RealIntegerType, is_constructible<RealIntegerType,
CompatibleNumberIntegerType>::value && CompatibleNumberIntegerType>::value &&
CompatibleLimits::is_integer && CompatibleLimits::is_integer &&
RealLimits::is_signed == CompatibleLimits::is_signed; RealLimits::is_signed == CompatibleLimits::is_signed;
...@@ -382,17 +427,10 @@ template<typename BasicJsonType, typename CompatibleType> ...@@ -382,17 +427,10 @@ template<typename BasicJsonType, typename CompatibleType>
struct is_compatible_type struct is_compatible_type
: is_compatible_type_impl<BasicJsonType, CompatibleType> {}; : is_compatible_type_impl<BasicJsonType, CompatibleType> {};
// https://en.cppreference.com/w/cpp/types/conjunction
template<class...> struct conjunction : std::true_type { };
template<class B1> struct conjunction<B1> : B1 { };
template<class B1, class... Bn>
struct conjunction<B1, Bn...>
: std::conditional<bool(B1::value), conjunction<Bn...>, B1>::type {};
template<typename T1, typename T2> template<typename T1, typename T2>
struct is_constructible_tuple : std::false_type {}; struct is_constructible_tuple : std::false_type {};
template<typename T1, typename... Args> template<typename T1, typename... Args>
struct is_constructible_tuple<T1, std::tuple<Args...>> : conjunction<std::is_constructible<T1, Args>...> {}; struct is_constructible_tuple<T1, std::tuple<Args...>> : conjunction<is_constructible<T1, Args>...> {};
} // namespace detail } // namespace detail
} // namespace nlohmann } // namespace nlohmann
This diff is collapsed.
This diff is collapsed.
...@@ -134,6 +134,29 @@ struct NotSerializableData ...@@ -134,6 +134,29 @@ struct NotSerializableData
}; };
/////////////////////////////////////////////////////////////////////
// for #2574
/////////////////////////////////////////////////////////////////////
struct NonDefaultConstructible
{
explicit NonDefaultConstructible (int a) : x(a) { }
int x;
};
namespace nlohmann
{
template <>
struct adl_serializer<NonDefaultConstructible>
{
static NonDefaultConstructible from_json (json const& j)
{
return NonDefaultConstructible(j.get<int>());
}
};
} // namespace nlohmann
TEST_CASE("regression tests 2") TEST_CASE("regression tests 2")
{ {
SECTION("issue #1001 - Fix memory leak during parser callback") SECTION("issue #1001 - Fix memory leak during parser callback")
...@@ -501,4 +524,74 @@ TEST_CASE("regression tests 2") ...@@ -501,4 +524,74 @@ TEST_CASE("regression tests 2")
CHECK(j.dump() == "\"Hello, world!\""); CHECK(j.dump() == "\"Hello, world!\"");
} }
#endif #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<std::array<NonDefaultConstructible, 2>>();
CHECK(arr[0].x == 7);
CHECK(arr[1].x == 4);
}
{
json j = 7;
CHECK_THROWS_AS((j.get<std::array<NonDefaultConstructible, 1>>()), json::type_error);
}
}
SECTION("std::pair")
{
{
json j = { 3, 8 };
auto p = j.get<std::pair<NonDefaultConstructible, NonDefaultConstructible>>();
CHECK(p.first.x == 3);
CHECK(p.second.x == 8);
}
{
json j = { 4, 1 };
auto p = j.get<std::pair<int, NonDefaultConstructible>>();
CHECK(p.first == 4);
CHECK(p.second.x == 1);
}
{
json j = { 6, 7 };
auto p = j.get<std::pair<NonDefaultConstructible, int>>();
CHECK(p.first.x == 6);
CHECK(p.second == 7);
}
{
json j = 7;
CHECK_THROWS_AS((j.get<std::pair<NonDefaultConstructible, int>>()), json::type_error);
}
}
SECTION("std::tuple")
{
{
json j = { 9 };
auto t = j.get<std::tuple<NonDefaultConstructible>>();
CHECK(std::get<0>(t).x == 9);
}
{
json j = { 9, 8, 7 };
auto t = j.get<std::tuple<NonDefaultConstructible, int, NonDefaultConstructible>>();
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<std::tuple<NonDefaultConstructible>>()), json::type_error);
}
}
}
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment