diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index 358b1c65..8f8162ff 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -84,13 +84,13 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) } template < - typename BasicJsonType, typename CompatibleStringType, + typename BasicJsonType, typename ConstructibleStringType, enable_if_t < - is_compatible_string_type<BasicJsonType, CompatibleStringType>::value and + is_constructible_string_type<BasicJsonType, ConstructibleStringType>::value and not std::is_same<typename BasicJsonType::string_t, - CompatibleStringType>::value, + ConstructibleStringType>::value, int > = 0 > -void from_json(const BasicJsonType& j, CompatibleStringType& s) +void from_json(const BasicJsonType& j, ConstructibleStringType& s) { if (JSON_UNLIKELY(not j.is_string())) { @@ -173,11 +173,11 @@ auto from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr, } } -template<typename BasicJsonType, typename CompatibleArrayType> -auto from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, priority_tag<1> /*unused*/) +template<typename BasicJsonType, typename ConstructibleArrayType> +auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/) -> decltype( - arr.reserve(std::declval<typename CompatibleArrayType::size_type>()), - j.template get<typename CompatibleArrayType::value_type>(), + arr.reserve(std::declval<typename ConstructibleArrayType::size_type>()), + j.template get<typename ConstructibleArrayType::value_type>(), void()) { using std::end; @@ -188,12 +188,12 @@ auto from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, prio { // get<BasicJsonType>() returns *this, this won't call a from_json // method when value_type is BasicJsonType - return i.template get<typename CompatibleArrayType::value_type>(); + return i.template get<typename ConstructibleArrayType::value_type>(); }); } -template <typename BasicJsonType, typename CompatibleArrayType> -void from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, +template <typename BasicJsonType, typename ConstructibleArrayType> +void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<0> /*unused*/) { using std::end; @@ -204,21 +204,21 @@ void from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, { // get<BasicJsonType>() returns *this, this won't call a from_json // method when value_type is BasicJsonType - return i.template get<typename CompatibleArrayType::value_type>(); + return i.template get<typename ConstructibleArrayType::value_type>(); }); } -template <typename BasicJsonType, typename CompatibleArrayType, +template <typename BasicJsonType, typename ConstructibleArrayType, enable_if_t < - is_compatible_array_type<BasicJsonType, CompatibleArrayType>::value and - not is_compatible_object_type<BasicJsonType, CompatibleArrayType>::value and - not is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value and - not is_basic_json<CompatibleArrayType>::value, + is_constructible_array_type<BasicJsonType, ConstructibleArrayType>::value and + not is_constructible_object_type<BasicJsonType, ConstructibleArrayType>::value and + not is_constructible_string_type<BasicJsonType, ConstructibleArrayType>::value and + not is_basic_json<ConstructibleArrayType>::value, int > = 0 > -auto from_json(const BasicJsonType& j, CompatibleArrayType& arr) +auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr) -> decltype(from_json_array_impl(j, arr, priority_tag<3> {}), -j.template get<typename CompatibleArrayType::value_type>(), +j.template get<typename ConstructibleArrayType::value_type>(), void()) { if (JSON_UNLIKELY(not j.is_array())) @@ -230,9 +230,9 @@ void()) from_json_array_impl(j, arr, priority_tag<3> {}); } -template<typename BasicJsonType, typename CompatibleObjectType, - enable_if_t<is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value, int> = 0> -void from_json(const BasicJsonType& j, CompatibleObjectType& obj) +template<typename BasicJsonType, typename ConstructibleObjectType, + enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0> +void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) { if (JSON_UNLIKELY(not j.is_object())) { @@ -240,13 +240,13 @@ void from_json(const BasicJsonType& j, CompatibleObjectType& obj) } auto inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>(); - using value_type = typename CompatibleObjectType::value_type; + using value_type = typename ConstructibleObjectType::value_type; std::transform( inner_object->begin(), inner_object->end(), std::inserter(obj, obj.begin()), [](typename BasicJsonType::object_t::value_type const & p) { - return value_type(p.first, p.second.template get<typename CompatibleObjectType::mapped_type>()); + return value_type(p.first, p.second.template get<typename ConstructibleObjectType::mapped_type>()); }); } diff --git a/include/nlohmann/detail/meta/type_traits.hpp b/include/nlohmann/detail/meta/type_traits.hpp index 1a1a1744..efe878f6 100644 --- a/include/nlohmann/detail/meta/type_traits.hpp +++ b/include/nlohmann/detail/meta/type_traits.hpp @@ -26,6 +26,15 @@ namespace detail // helpers // ///////////// +// Note to maintainers: +// +// Every trait in this file expects a non CV-qualified type. +// The only exceptions are in the 'aliases for detected' section +// (i.e. those of the form: decltype(T::member_function(std::declval<T>()))) +// +// In this case, T has to be properly CV-qualified to constraint the function arguments +// (e.g. to_json(BasicJsonType&, const T&)) + template<typename> struct is_basic_json : std::false_type {}; NLOHMANN_BASIC_JSON_TPL_DECLARATION @@ -68,6 +77,52 @@ using from_json_function = decltype(T::from_json(std::declval<Args>()...)); template <typename T, typename U> using get_template_function = decltype(std::declval<T>().template get<U>()); +// trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists +template <typename BasicJsonType, typename T, typename = void> +struct has_from_json : std::false_type {}; + +template <typename BasicJsonType, typename T> +struct has_from_json<BasicJsonType, T, + enable_if_t<not is_basic_json<T>::value>> +{ + using serializer = typename BasicJsonType::template json_serializer<T, void>; + + static constexpr bool value = + is_detected_exact<void, from_json_function, serializer, + const BasicJsonType&, T&>::value; +}; + +// This trait checks if JSONSerializer<T>::from_json(json const&) exists +// this overload is used for non-default-constructible user-defined-types +template <typename BasicJsonType, typename T, typename = void> +struct has_non_default_from_json : std::false_type {}; + +template<typename BasicJsonType, typename T> +struct has_non_default_from_json<BasicJsonType, T, enable_if_t<not is_basic_json<T>::value>> +{ + using serializer = typename BasicJsonType::template json_serializer<T, void>; + + static constexpr bool value = + is_detected_exact<T, from_json_function, serializer, + const BasicJsonType&>::value; +}; + +// This trait checks if BasicJsonType::json_serializer<T>::to_json exists +// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion. +template <typename BasicJsonType, typename T, typename = void> +struct has_to_json : std::false_type {}; + +template <typename BasicJsonType, typename T> +struct has_to_json<BasicJsonType, T, enable_if_t<not is_basic_json<T>::value>> +{ + using serializer = typename BasicJsonType::template json_serializer<T, void>; + + static constexpr bool value = + is_detected_exact<void, to_json_function, serializer, BasicJsonType&, + T>::value; +}; + + /////////////////// // is_ functions // /////////////////// @@ -123,6 +178,35 @@ template <typename BasicJsonType, typename CompatibleObjectType> struct is_compatible_object_type : is_compatible_object_type_impl<BasicJsonType, CompatibleObjectType> {}; +template <typename BasicJsonType, typename ConstructibleObjectType, + typename = void> +struct is_constructible_object_type_impl : std::false_type {}; + +template <typename BasicJsonType, typename ConstructibleObjectType> +struct is_constructible_object_type_impl < + BasicJsonType, ConstructibleObjectType, + enable_if_t<is_detected<mapped_type_t, ConstructibleObjectType>::value and + is_detected<key_type_t, ConstructibleObjectType>::value >> +{ + using object_t = typename BasicJsonType::object_t; + + static constexpr bool value = + std::is_constructible<typename ConstructibleObjectType::key_type, + typename object_t::key_type>::value and + std::is_same<typename object_t::mapped_type, + typename ConstructibleObjectType::mapped_type>::value or + (has_from_json<BasicJsonType, + typename ConstructibleObjectType::mapped_type>::value or + has_non_default_from_json < + BasicJsonType, + typename ConstructibleObjectType::mapped_type >::value); +}; + +template <typename BasicJsonType, typename ConstructibleObjectType> +struct is_constructible_object_type + : is_constructible_object_type_impl<BasicJsonType, + ConstructibleObjectType> {}; + template <typename BasicJsonType, typename CompatibleStringType, typename = void> struct is_compatible_string_type_impl : std::false_type {}; @@ -137,9 +221,28 @@ struct is_compatible_string_type_impl < std::is_constructible<typename BasicJsonType::string_t, CompatibleStringType>::value; }; -template <typename BasicJsonType, typename CompatibleStringType> +template <typename BasicJsonType, typename ConstrutibleStringType> struct is_compatible_string_type - : is_compatible_string_type_impl<BasicJsonType, CompatibleStringType> {}; + : is_compatible_string_type_impl<BasicJsonType, ConstrutibleStringType> {}; + +template <typename BasicJsonType, typename ConstrutibleStringType, + typename = void> +struct is_constructible_string_type_impl : std::false_type {}; + +template <typename BasicJsonType, typename ConstrutibleStringType> +struct is_constructible_string_type_impl < + BasicJsonType, ConstrutibleStringType, + enable_if_t<is_detected_exact<typename BasicJsonType::string_t::value_type, + value_type_t, ConstrutibleStringType>::value >> +{ + static constexpr auto value = + std::is_constructible<ConstrutibleStringType, + typename BasicJsonType::string_t>::value; +}; + +template <typename BasicJsonType, typename ConstrutibleStringType> +struct is_constructible_string_type + : is_constructible_string_type_impl<BasicJsonType, ConstrutibleStringType> {}; template <typename BasicJsonType, typename CompatibleArrayType, typename = void> struct is_compatible_array_type_impl : std::false_type {}; @@ -148,18 +251,61 @@ template <typename BasicJsonType, typename CompatibleArrayType> struct is_compatible_array_type_impl < BasicJsonType, CompatibleArrayType, enable_if_t<is_detected<value_type_t, CompatibleArrayType>::value and - is_detected<iterator_t, CompatibleArrayType>::value >> + is_detected<iterator_t, CompatibleArrayType>::value and +// This is needed because json_reverse_iterator has a ::iterator type... +// Therefore it is detected as a CompatibleArrayType. +// The real fix would be to have an Iterable concept. + not is_iterator_traits< + std::iterator_traits<CompatibleArrayType>>::value >> { - // This is needed because json_reverse_iterator has a ::iterator type... - // Therefore it is detected as a CompatibleArrayType. - // The real fix would be to have an Iterable concept. - static constexpr bool value = not is_iterator_traits<std::iterator_traits<CompatibleArrayType>>::value; + static constexpr bool value = + std::is_constructible<BasicJsonType, + typename CompatibleArrayType::value_type>::value; }; template <typename BasicJsonType, typename CompatibleArrayType> struct is_compatible_array_type : is_compatible_array_type_impl<BasicJsonType, CompatibleArrayType> {}; +template <typename BasicJsonType, typename ConstructibleArrayType, typename = void> +struct is_constructible_array_type_impl : std::false_type {}; + +template <typename BasicJsonType, typename ConstructibleArrayType> +struct is_constructible_array_type_impl < + BasicJsonType, ConstructibleArrayType, + enable_if_t<std::is_same<ConstructibleArrayType, + typename BasicJsonType::value_type>::value >> + : std::true_type {}; + +template <typename BasicJsonType, typename ConstructibleArrayType> +struct is_constructible_array_type_impl < + BasicJsonType, ConstructibleArrayType, + enable_if_t<not std::is_same<ConstructibleArrayType, + typename BasicJsonType::value_type>::value and + is_detected<value_type_t, ConstructibleArrayType>::value and + is_detected<iterator_t, ConstructibleArrayType>::value and + is_complete_type< + detected_t<value_type_t, ConstructibleArrayType>>::value >> +{ + static constexpr bool value = + // This is needed because json_reverse_iterator has a ::iterator type, + // furthermore, std::back_insert_iterator (and other iterators) have a base class `iterator`... + // Therefore it is detected as a ConstructibleArrayType. + // The real fix would be to have an Iterable concept. + not is_iterator_traits < + std::iterator_traits<ConstructibleArrayType >>::value and + + (std::is_same<typename ConstructibleArrayType::value_type, typename BasicJsonType::array_t::value_type>::value or + has_from_json<BasicJsonType, + typename ConstructibleArrayType::value_type>::value or + has_non_default_from_json < + BasicJsonType, typename ConstructibleArrayType::value_type >::value); +}; + +template <typename BasicJsonType, typename ConstructibleArrayType> +struct is_constructible_array_type + : is_constructible_array_type_impl<BasicJsonType, ConstructibleArrayType> {}; + template <typename RealIntegerType, typename CompatibleNumberIntegerType, typename = void> struct is_compatible_integer_type_impl : std::false_type {}; @@ -187,51 +333,6 @@ struct is_compatible_integer_type : is_compatible_integer_type_impl<RealIntegerType, CompatibleNumberIntegerType> {}; -// trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists -template <typename BasicJsonType, typename T, typename = void> -struct has_from_json : std::false_type {}; - -template <typename BasicJsonType, typename T> -struct has_from_json<BasicJsonType, T, - enable_if_t<not is_basic_json<T>::value>> -{ - using serializer = typename BasicJsonType::template json_serializer<T, void>; - - static constexpr bool value = - is_detected_exact<void, from_json_function, serializer, - const BasicJsonType&, T&>::value; -}; - -// This trait checks if JSONSerializer<T>::from_json(json const&) exists -// this overload is used for non-default-constructible user-defined-types -template <typename BasicJsonType, typename T, typename = void> -struct has_non_default_from_json : std::false_type {}; - -template<typename BasicJsonType, typename T> -struct has_non_default_from_json<BasicJsonType, T, enable_if_t<not is_basic_json<T>::value>> -{ - using serializer = typename BasicJsonType::template json_serializer<T, void>; - - static constexpr bool value = - is_detected_exact<T, from_json_function, serializer, - const BasicJsonType&>::value; -}; - -// This trait checks if BasicJsonType::json_serializer<T>::to_json exists -// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion. -template <typename BasicJsonType, typename T, typename = void> -struct has_to_json : std::false_type {}; - -template <typename BasicJsonType, typename T> -struct has_to_json<BasicJsonType, T, enable_if_t<not is_basic_json<T>::value>> -{ - using serializer = typename BasicJsonType::template json_serializer<T, void>; - - static constexpr bool value = - is_detected_exact<void, to_json_function, serializer, BasicJsonType&, - T>::value; -}; - template <typename BasicJsonType, typename CompatibleType, typename = void> struct is_compatible_type_impl: std::false_type {}; diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index f744366c..1de78dfc 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -386,6 +386,15 @@ namespace detail // helpers // ///////////// +// Note to maintainers: +// +// Every trait in this file expects a non CV-qualified type. +// The only exceptions are in the 'aliases for detected' section +// (i.e. those of the form: decltype(T::member_function(std::declval<T>()))) +// +// In this case, T has to be properly CV-qualified to constraint the function arguments +// (e.g. to_json(BasicJsonType&, const T&)) + template<typename> struct is_basic_json : std::false_type {}; NLOHMANN_BASIC_JSON_TPL_DECLARATION @@ -428,6 +437,52 @@ using from_json_function = decltype(T::from_json(std::declval<Args>()...)); template <typename T, typename U> using get_template_function = decltype(std::declval<T>().template get<U>()); +// trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists +template <typename BasicJsonType, typename T, typename = void> +struct has_from_json : std::false_type {}; + +template <typename BasicJsonType, typename T> +struct has_from_json<BasicJsonType, T, + enable_if_t<not is_basic_json<T>::value>> +{ + using serializer = typename BasicJsonType::template json_serializer<T, void>; + + static constexpr bool value = + is_detected_exact<void, from_json_function, serializer, + const BasicJsonType&, T&>::value; +}; + +// This trait checks if JSONSerializer<T>::from_json(json const&) exists +// this overload is used for non-default-constructible user-defined-types +template <typename BasicJsonType, typename T, typename = void> +struct has_non_default_from_json : std::false_type {}; + +template<typename BasicJsonType, typename T> +struct has_non_default_from_json<BasicJsonType, T, enable_if_t<not is_basic_json<T>::value>> +{ + using serializer = typename BasicJsonType::template json_serializer<T, void>; + + static constexpr bool value = + is_detected_exact<T, from_json_function, serializer, + const BasicJsonType&>::value; +}; + +// This trait checks if BasicJsonType::json_serializer<T>::to_json exists +// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion. +template <typename BasicJsonType, typename T, typename = void> +struct has_to_json : std::false_type {}; + +template <typename BasicJsonType, typename T> +struct has_to_json<BasicJsonType, T, enable_if_t<not is_basic_json<T>::value>> +{ + using serializer = typename BasicJsonType::template json_serializer<T, void>; + + static constexpr bool value = + is_detected_exact<void, to_json_function, serializer, BasicJsonType&, + T>::value; +}; + + /////////////////// // is_ functions // /////////////////// @@ -483,6 +538,35 @@ template <typename BasicJsonType, typename CompatibleObjectType> struct is_compatible_object_type : is_compatible_object_type_impl<BasicJsonType, CompatibleObjectType> {}; +template <typename BasicJsonType, typename ConstructibleObjectType, + typename = void> +struct is_constructible_object_type_impl : std::false_type {}; + +template <typename BasicJsonType, typename ConstructibleObjectType> +struct is_constructible_object_type_impl < + BasicJsonType, ConstructibleObjectType, + enable_if_t<is_detected<mapped_type_t, ConstructibleObjectType>::value and + is_detected<key_type_t, ConstructibleObjectType>::value >> +{ + using object_t = typename BasicJsonType::object_t; + + static constexpr bool value = + std::is_constructible<typename ConstructibleObjectType::key_type, + typename object_t::key_type>::value and + std::is_same<typename object_t::mapped_type, + typename ConstructibleObjectType::mapped_type>::value or + (has_from_json<BasicJsonType, + typename ConstructibleObjectType::mapped_type>::value or + has_non_default_from_json < + BasicJsonType, + typename ConstructibleObjectType::mapped_type >::value); +}; + +template <typename BasicJsonType, typename ConstructibleObjectType> +struct is_constructible_object_type + : is_constructible_object_type_impl<BasicJsonType, + ConstructibleObjectType> {}; + template <typename BasicJsonType, typename CompatibleStringType, typename = void> struct is_compatible_string_type_impl : std::false_type {}; @@ -497,9 +581,28 @@ struct is_compatible_string_type_impl < std::is_constructible<typename BasicJsonType::string_t, CompatibleStringType>::value; }; -template <typename BasicJsonType, typename CompatibleStringType> +template <typename BasicJsonType, typename ConstrutibleStringType> struct is_compatible_string_type - : is_compatible_string_type_impl<BasicJsonType, CompatibleStringType> {}; + : is_compatible_string_type_impl<BasicJsonType, ConstrutibleStringType> {}; + +template <typename BasicJsonType, typename ConstrutibleStringType, + typename = void> +struct is_constructible_string_type_impl : std::false_type {}; + +template <typename BasicJsonType, typename ConstrutibleStringType> +struct is_constructible_string_type_impl < + BasicJsonType, ConstrutibleStringType, + enable_if_t<is_detected_exact<typename BasicJsonType::string_t::value_type, + value_type_t, ConstrutibleStringType>::value >> +{ + static constexpr auto value = + std::is_constructible<ConstrutibleStringType, + typename BasicJsonType::string_t>::value; +}; + +template <typename BasicJsonType, typename ConstrutibleStringType> +struct is_constructible_string_type + : is_constructible_string_type_impl<BasicJsonType, ConstrutibleStringType> {}; template <typename BasicJsonType, typename CompatibleArrayType, typename = void> struct is_compatible_array_type_impl : std::false_type {}; @@ -508,18 +611,61 @@ template <typename BasicJsonType, typename CompatibleArrayType> struct is_compatible_array_type_impl < BasicJsonType, CompatibleArrayType, enable_if_t<is_detected<value_type_t, CompatibleArrayType>::value and - is_detected<iterator_t, CompatibleArrayType>::value >> + is_detected<iterator_t, CompatibleArrayType>::value and +// This is needed because json_reverse_iterator has a ::iterator type... +// Therefore it is detected as a CompatibleArrayType. +// The real fix would be to have an Iterable concept. + not is_iterator_traits< + std::iterator_traits<CompatibleArrayType>>::value >> { - // This is needed because json_reverse_iterator has a ::iterator type... - // Therefore it is detected as a CompatibleArrayType. - // The real fix would be to have an Iterable concept. - static constexpr bool value = not is_iterator_traits<std::iterator_traits<CompatibleArrayType>>::value; + static constexpr bool value = + std::is_constructible<BasicJsonType, + typename CompatibleArrayType::value_type>::value; }; template <typename BasicJsonType, typename CompatibleArrayType> struct is_compatible_array_type : is_compatible_array_type_impl<BasicJsonType, CompatibleArrayType> {}; +template <typename BasicJsonType, typename ConstructibleArrayType, typename = void> +struct is_constructible_array_type_impl : std::false_type {}; + +template <typename BasicJsonType, typename ConstructibleArrayType> +struct is_constructible_array_type_impl < + BasicJsonType, ConstructibleArrayType, + enable_if_t<std::is_same<ConstructibleArrayType, + typename BasicJsonType::value_type>::value >> + : std::true_type {}; + +template <typename BasicJsonType, typename ConstructibleArrayType> +struct is_constructible_array_type_impl < + BasicJsonType, ConstructibleArrayType, + enable_if_t<not std::is_same<ConstructibleArrayType, + typename BasicJsonType::value_type>::value and + is_detected<value_type_t, ConstructibleArrayType>::value and + is_detected<iterator_t, ConstructibleArrayType>::value and + is_complete_type< + detected_t<value_type_t, ConstructibleArrayType>>::value >> +{ + static constexpr bool value = + // This is needed because json_reverse_iterator has a ::iterator type, + // furthermore, std::back_insert_iterator (and other iterators) have a base class `iterator`... + // Therefore it is detected as a ConstructibleArrayType. + // The real fix would be to have an Iterable concept. + not is_iterator_traits < + std::iterator_traits<ConstructibleArrayType >>::value and + + (std::is_same<typename ConstructibleArrayType::value_type, typename BasicJsonType::array_t::value_type>::value or + has_from_json<BasicJsonType, + typename ConstructibleArrayType::value_type>::value or + has_non_default_from_json < + BasicJsonType, typename ConstructibleArrayType::value_type >::value); +}; + +template <typename BasicJsonType, typename ConstructibleArrayType> +struct is_constructible_array_type + : is_constructible_array_type_impl<BasicJsonType, ConstructibleArrayType> {}; + template <typename RealIntegerType, typename CompatibleNumberIntegerType, typename = void> struct is_compatible_integer_type_impl : std::false_type {}; @@ -547,51 +693,6 @@ struct is_compatible_integer_type : is_compatible_integer_type_impl<RealIntegerType, CompatibleNumberIntegerType> {}; -// trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists -template <typename BasicJsonType, typename T, typename = void> -struct has_from_json : std::false_type {}; - -template <typename BasicJsonType, typename T> -struct has_from_json<BasicJsonType, T, - enable_if_t<not is_basic_json<T>::value>> -{ - using serializer = typename BasicJsonType::template json_serializer<T, void>; - - static constexpr bool value = - is_detected_exact<void, from_json_function, serializer, - const BasicJsonType&, T&>::value; -}; - -// This trait checks if JSONSerializer<T>::from_json(json const&) exists -// this overload is used for non-default-constructible user-defined-types -template <typename BasicJsonType, typename T, typename = void> -struct has_non_default_from_json : std::false_type {}; - -template<typename BasicJsonType, typename T> -struct has_non_default_from_json<BasicJsonType, T, enable_if_t<not is_basic_json<T>::value>> -{ - using serializer = typename BasicJsonType::template json_serializer<T, void>; - - static constexpr bool value = - is_detected_exact<T, from_json_function, serializer, - const BasicJsonType&>::value; -}; - -// This trait checks if BasicJsonType::json_serializer<T>::to_json exists -// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion. -template <typename BasicJsonType, typename T, typename = void> -struct has_to_json : std::false_type {}; - -template <typename BasicJsonType, typename T> -struct has_to_json<BasicJsonType, T, enable_if_t<not is_basic_json<T>::value>> -{ - using serializer = typename BasicJsonType::template json_serializer<T, void>; - - static constexpr bool value = - is_detected_exact<void, to_json_function, serializer, BasicJsonType&, - T>::value; -}; - template <typename BasicJsonType, typename CompatibleType, typename = void> struct is_compatible_type_impl: std::false_type {}; @@ -1156,13 +1257,13 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) } template < - typename BasicJsonType, typename CompatibleStringType, + typename BasicJsonType, typename ConstructibleStringType, enable_if_t < - is_compatible_string_type<BasicJsonType, CompatibleStringType>::value and + is_constructible_string_type<BasicJsonType, ConstructibleStringType>::value and not std::is_same<typename BasicJsonType::string_t, - CompatibleStringType>::value, + ConstructibleStringType>::value, int > = 0 > -void from_json(const BasicJsonType& j, CompatibleStringType& s) +void from_json(const BasicJsonType& j, ConstructibleStringType& s) { if (JSON_UNLIKELY(not j.is_string())) { @@ -1245,11 +1346,11 @@ auto from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr, } } -template<typename BasicJsonType, typename CompatibleArrayType> -auto from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, priority_tag<1> /*unused*/) +template<typename BasicJsonType, typename ConstructibleArrayType> +auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/) -> decltype( - arr.reserve(std::declval<typename CompatibleArrayType::size_type>()), - j.template get<typename CompatibleArrayType::value_type>(), + arr.reserve(std::declval<typename ConstructibleArrayType::size_type>()), + j.template get<typename ConstructibleArrayType::value_type>(), void()) { using std::end; @@ -1260,12 +1361,12 @@ auto from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, prio { // get<BasicJsonType>() returns *this, this won't call a from_json // method when value_type is BasicJsonType - return i.template get<typename CompatibleArrayType::value_type>(); + return i.template get<typename ConstructibleArrayType::value_type>(); }); } -template <typename BasicJsonType, typename CompatibleArrayType> -void from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, +template <typename BasicJsonType, typename ConstructibleArrayType> +void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<0> /*unused*/) { using std::end; @@ -1276,21 +1377,21 @@ void from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, { // get<BasicJsonType>() returns *this, this won't call a from_json // method when value_type is BasicJsonType - return i.template get<typename CompatibleArrayType::value_type>(); + return i.template get<typename ConstructibleArrayType::value_type>(); }); } -template <typename BasicJsonType, typename CompatibleArrayType, +template <typename BasicJsonType, typename ConstructibleArrayType, enable_if_t < - is_compatible_array_type<BasicJsonType, CompatibleArrayType>::value and - not is_compatible_object_type<BasicJsonType, CompatibleArrayType>::value and - not is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value and - not is_basic_json<CompatibleArrayType>::value, + is_constructible_array_type<BasicJsonType, ConstructibleArrayType>::value and + not is_constructible_object_type<BasicJsonType, ConstructibleArrayType>::value and + not is_constructible_string_type<BasicJsonType, ConstructibleArrayType>::value and + not is_basic_json<ConstructibleArrayType>::value, int > = 0 > -auto from_json(const BasicJsonType& j, CompatibleArrayType& arr) +auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr) -> decltype(from_json_array_impl(j, arr, priority_tag<3> {}), -j.template get<typename CompatibleArrayType::value_type>(), +j.template get<typename ConstructibleArrayType::value_type>(), void()) { if (JSON_UNLIKELY(not j.is_array())) @@ -1302,9 +1403,9 @@ void()) from_json_array_impl(j, arr, priority_tag<3> {}); } -template<typename BasicJsonType, typename CompatibleObjectType, - enable_if_t<is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value, int> = 0> -void from_json(const BasicJsonType& j, CompatibleObjectType& obj) +template<typename BasicJsonType, typename ConstructibleObjectType, + enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0> +void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) { if (JSON_UNLIKELY(not j.is_object())) { @@ -1312,13 +1413,13 @@ void from_json(const BasicJsonType& j, CompatibleObjectType& obj) } auto inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>(); - using value_type = typename CompatibleObjectType::value_type; + using value_type = typename ConstructibleObjectType::value_type; std::transform( inner_object->begin(), inner_object->end(), std::inserter(obj, obj.begin()), [](typename BasicJsonType::object_t::value_type const & p) { - return value_type(p.first, p.second.template get<typename CompatibleObjectType::mapped_type>()); + return value_type(p.first, p.second.template get<typename ConstructibleObjectType::mapped_type>()); }); } diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp index 259377aa..2c21576d 100644 --- a/test/src/unit-regression.cpp +++ b/test/src/unit-regression.cpp @@ -121,6 +121,28 @@ struct nocopy j = {{"val", n.val}}; } }; + +struct Data +{ + std::string a; + std::string b; +}; + +void from_json(const json& j, Data& data) +{ + j["a"].get_to(data.a); + j["b"].get_to(data.b); +} + +bool operator==(Data const& lhs, Data const& rhs) +{ + return lhs.a == rhs.a && lhs.b == rhs.b; +} + +bool operator!=(Data const& lhs, Data const& rhs) +{ + return !(lhs == rhs); +} } ///////////////////////////////////////////////////////////////////// @@ -1665,4 +1687,24 @@ TEST_CASE("regression tests") not std::is_constructible<json, std::variant<int, float>>::value, ""); } #endif + + SECTION("issue #1299 - compile error in from_json converting to container " + "with std::pair") + { + json j = + { + {"1", {{"a", "testa_1"}, {"b", "testb_1"}}}, + {"2", {{"a", "testa_2"}, {"b", "testb_2"}}}, + {"3", {{"a", "testa_3"}, {"b", "testb_3"}}}, + }; + + std::map<std::string, Data> expected + { + {"1", {"testa_1", "testb_1" }}, + {"2", {"testa_2", "testb_2"}}, + {"3", {"testa_3", "testb_3"}}, + }; + const auto data = j.get<decltype(expected)>(); + CHECK(expected == data); + } }