add new is_constructible_* traits used in from_json
is_compatible_* traits were used in from_json, but it made no sense whatsoever. It used to work because of non-SFINAE correctness + json_ref unconstrained variadic template constructor. SFINAE checks are becoming quite complex, we need a specification of some sort describing: * which concepts the library uses * how the conversion to/from json works in detail Having such a specification would really help simplifying the current code (as well as having meaningful checks). Fixes !1299
This commit is contained in:
		
							parent
							
								
									dd672939a0
								
							
						
					
					
						commit
						45c8af2c46
					
				
					 4 changed files with 378 additions and 152 deletions
				
			
		| 
						 | 
				
			
			@ -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>());
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -68,6 +68,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 +169,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 +212,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 +242,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 +324,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 {};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue