Fix issue #1237

* Make the conversion operator SFINAE correct. * Workaround a GCC bug with some traits in type_traits.hpp The first bullet-point implies that every `get`/`get_ptr` be SFINAE correct as well.
parent e4bc98d0
...@@ -65,6 +65,9 @@ using to_json_function = decltype(T::to_json(std::declval<Args>()...)); ...@@ -65,6 +65,9 @@ using to_json_function = decltype(T::to_json(std::declval<Args>()...));
template <typename T, typename... Args> template <typename T, typename... Args>
using from_json_function = decltype(T::from_json(std::declval<Args>()...)); 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>());
/////////////////// ///////////////////
// is_ functions // // is_ functions //
/////////////////// ///////////////////
...@@ -185,8 +188,12 @@ struct is_compatible_integer_type ...@@ -185,8 +188,12 @@ struct is_compatible_integer_type
CompatibleNumberIntegerType> {}; CompatibleNumberIntegerType> {};
// trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists // trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists
template<typename BasicJsonType, typename T> template <typename BasicJsonType, typename T, typename = void>
struct has_from_json 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>; using serializer = typename BasicJsonType::template json_serializer<T, void>;
...@@ -197,8 +204,11 @@ struct has_from_json ...@@ -197,8 +204,11 @@ struct has_from_json
// This trait checks if JSONSerializer<T>::from_json(json const&) exists // This trait checks if JSONSerializer<T>::from_json(json const&) exists
// this overload is used for non-default-constructible user-defined-types // 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> template<typename BasicJsonType, typename T>
struct has_non_default_from_json 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>; using serializer = typename BasicJsonType::template json_serializer<T, void>;
...@@ -208,8 +218,12 @@ struct has_non_default_from_json ...@@ -208,8 +218,12 @@ struct has_non_default_from_json
}; };
// This trait checks if BasicJsonType::json_serializer<T>::to_json exists // This trait checks if BasicJsonType::json_serializer<T>::to_json exists
template<typename BasicJsonType, typename T> // Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion.
struct has_to_json 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>; using serializer = typename BasicJsonType::template json_serializer<T, void>;
......
...@@ -2624,17 +2624,18 @@ class basic_json ...@@ -2624,17 +2624,18 @@ class basic_json
} }
/*! /*!
@brief get a pointer value (explicit) @brief get a pointer value (implicit)
Explicit pointer access to the internally stored JSON value. No copies are Implicit pointer access to the internally stored JSON value. No copies are
made. made.
@warning The pointer becomes invalid if the underlying JSON object @warning Writing data to the pointee of the result yields an undefined
changes. state.
@tparam PointerType pointer type; must be a pointer to @ref array_t, @ref @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, object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,
@ref number_unsigned_t, or @ref number_float_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 @return pointer to the internally stored JSON value if the requested
pointer type @a PointerType fits to the JSON value; `nullptr` otherwise pointer type @a PointerType fits to the JSON value; `nullptr` otherwise
...@@ -2644,45 +2645,43 @@ class basic_json ...@@ -2644,45 +2645,43 @@ class basic_json
@liveexample{The example below shows how pointers to internal values of a @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 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 `nullptr` is returned if the value and the requested pointer type does not
match.,get__PointerType} match.,get_ptr}
@sa @ref get_ptr() for explicit pointer-member access
@since version 1.0.0 @since version 1.0.0
*/ */
template<typename PointerType, typename std::enable_if< template<typename PointerType, typename std::enable_if<
std::is_pointer<PointerType>::value, int>::type = 0> std::is_pointer<PointerType>::value, int>::type = 0>
PointerType get() noexcept auto get_ptr() noexcept -> decltype(std::declval<basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))
{ {
// delegate the call to get_ptr // delegate the call to get_impl_ptr<>()
return get_ptr<PointerType>(); return get_impl_ptr(static_cast<PointerType>(nullptr));
} }
/*! /*!
@brief get a pointer value (explicit) @brief get a pointer value (implicit)
@copydoc get() @copydoc get_ptr()
*/ */
template<typename PointerType, typename std::enable_if< template<typename PointerType, typename std::enable_if<
std::is_pointer<PointerType>::value, int>::type = 0> std::is_pointer<PointerType>::value and
constexpr const PointerType get() const noexcept std::is_const<typename std::remove_pointer<PointerType>::type>::value, int>::type = 0>
constexpr auto get_ptr() const noexcept -> decltype(std::declval<const basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))
{ {
// delegate the call to get_ptr // delegate the call to get_impl_ptr<>() const
return get_ptr<PointerType>(); return get_impl_ptr(static_cast<PointerType>(nullptr));
} }
/*! /*!
@brief get a pointer value (implicit) @brief get a pointer value (explicit)
Implicit pointer access to the internally stored JSON value. No copies are Explicit pointer access to the internally stored JSON value. No copies are
made. made.
@warning Writing data to the pointee of the result yields an undefined @warning The pointer becomes invalid if the underlying JSON object
state. changes.
@tparam PointerType pointer type; must be a pointer to @ref array_t, @ref @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, 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 @ref number_unsigned_t, or @ref number_float_t.
assertion.
@return pointer to the internally stored JSON value if the requested @return pointer to the internally stored JSON value if the requested
pointer type @a PointerType fits to the JSON value; `nullptr` otherwise pointer type @a PointerType fits to the JSON value; `nullptr` otherwise
...@@ -2692,59 +2691,30 @@ class basic_json ...@@ -2692,59 +2691,30 @@ class basic_json
@liveexample{The example below shows how pointers to internal values of a @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 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 `nullptr` is returned if the value and the requested pointer type does not
match.,get_ptr} match.,get__PointerType}
@sa @ref get_ptr() for explicit pointer-member access
@since version 1.0.0 @since version 1.0.0
*/ */
template<typename PointerType, typename std::enable_if< template<typename PointerType, typename std::enable_if<
std::is_pointer<PointerType>::value, int>::type = 0> std::is_pointer<PointerType>::value, int>::type = 0>
PointerType get_ptr() noexcept auto get() noexcept -> decltype(std::declval<basic_json_t&>().template get_ptr<PointerType>())
{ {
// get the type of the PointerType (remove pointer and const) // delegate the call to get_ptr
using pointee_t = typename std::remove_const<typename return get_ptr<PointerType>();
std::remove_pointer<typename
std::remove_const<PointerType>::type>::type>::type;
// make sure the type matches the allowed types
static_assert(
std::is_same<object_t, pointee_t>::value
or std::is_same<array_t, pointee_t>::value
or std::is_same<string_t, pointee_t>::value
or std::is_same<boolean_t, pointee_t>::value
or std::is_same<number_integer_t, pointee_t>::value
or std::is_same<number_unsigned_t, pointee_t>::value
or std::is_same<number_float_t, pointee_t>::value
, "incompatible pointer type");
// delegate the call to get_impl_ptr<>()
return get_impl_ptr(static_cast<PointerType>(nullptr));
} }
/*! /*!
@brief get a pointer value (implicit) @brief get a pointer value (explicit)
@copydoc get_ptr() @copydoc get()
*/ */
template<typename PointerType, typename std::enable_if< template<typename PointerType, typename std::enable_if<
std::is_pointer<PointerType>::value and std::is_pointer<PointerType>::value, int>::type = 0>
std::is_const<typename std::remove_pointer<PointerType>::type>::value, int>::type = 0> constexpr auto get() const noexcept -> decltype(std::declval<const basic_json_t&>().template get_ptr<PointerType>())
constexpr const PointerType get_ptr() const noexcept {
{ // delegate the call to get_ptr
// get the type of the PointerType (remove pointer and const) return get_ptr<PointerType>();
using pointee_t = typename std::remove_const<typename
std::remove_pointer<typename
std::remove_const<PointerType>::type>::type>::type;
// make sure the type matches the allowed types
static_assert(
std::is_same<object_t, pointee_t>::value
or std::is_same<array_t, pointee_t>::value
or std::is_same<string_t, pointee_t>::value
or std::is_same<boolean_t, pointee_t>::value
or std::is_same<number_integer_t, pointee_t>::value
or std::is_same<number_unsigned_t, pointee_t>::value
or std::is_same<number_float_t, pointee_t>::value
, "incompatible pointer type");
// delegate the call to get_impl_ptr<>() const
return get_impl_ptr(static_cast<PointerType>(nullptr));
} }
/*! /*!
...@@ -2828,12 +2798,14 @@ class basic_json ...@@ -2828,12 +2798,14 @@ class basic_json
not std::is_same<ValueType, detail::json_ref<basic_json>>::value and not std::is_same<ValueType, detail::json_ref<basic_json>>::value and
not std::is_same<ValueType, typename string_t::value_type>::value and not std::is_same<ValueType, typename string_t::value_type>::value and
not detail::is_basic_json<ValueType>::value not detail::is_basic_json<ValueType>::value
#ifndef _MSC_VER // fix for issue #167 operator<< ambiguity under VS2015 #ifndef _MSC_VER // fix for issue #167 operator<< ambiguity under VS2015
and not std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>::value and not std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>::value
#if defined(JSON_HAS_CPP_17) && defined(_MSC_VER) and _MSC_VER <= 1914 #if defined(JSON_HAS_CPP_17) && defined(_MSC_VER) and _MSC_VER <= 1914
and not std::is_same<ValueType, typename std::string_view>::value and not std::is_same<ValueType, typename std::string_view>::value
#endif #endif
#endif #endif
and detail::is_detected<detail::get_template_function, const basic_json_t&, ValueType>::value
, int >::type = 0 > , int >::type = 0 >
operator ValueType() const operator ValueType() const
{ {
......
...@@ -425,6 +425,9 @@ using to_json_function = decltype(T::to_json(std::declval<Args>()...)); ...@@ -425,6 +425,9 @@ using to_json_function = decltype(T::to_json(std::declval<Args>()...));
template <typename T, typename... Args> template <typename T, typename... Args>
using from_json_function = decltype(T::from_json(std::declval<Args>()...)); 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>());
/////////////////// ///////////////////
// is_ functions // // is_ functions //
/////////////////// ///////////////////
...@@ -545,8 +548,12 @@ struct is_compatible_integer_type ...@@ -545,8 +548,12 @@ struct is_compatible_integer_type
CompatibleNumberIntegerType> {}; CompatibleNumberIntegerType> {};
// trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists // trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists
template<typename BasicJsonType, typename T> template <typename BasicJsonType, typename T, typename = void>
struct has_from_json 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>; using serializer = typename BasicJsonType::template json_serializer<T, void>;
...@@ -557,8 +564,11 @@ struct has_from_json ...@@ -557,8 +564,11 @@ struct has_from_json
// This trait checks if JSONSerializer<T>::from_json(json const&) exists // This trait checks if JSONSerializer<T>::from_json(json const&) exists
// this overload is used for non-default-constructible user-defined-types // 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> template<typename BasicJsonType, typename T>
struct has_non_default_from_json 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>; using serializer = typename BasicJsonType::template json_serializer<T, void>;
...@@ -568,8 +578,12 @@ struct has_non_default_from_json ...@@ -568,8 +578,12 @@ struct has_non_default_from_json
}; };
// This trait checks if BasicJsonType::json_serializer<T>::to_json exists // This trait checks if BasicJsonType::json_serializer<T>::to_json exists
template<typename BasicJsonType, typename T> // Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion.
struct has_to_json 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>; using serializer = typename BasicJsonType::template json_serializer<T, void>;
...@@ -13697,17 +13711,18 @@ class basic_json ...@@ -13697,17 +13711,18 @@ class basic_json
} }
/*! /*!
@brief get a pointer value (explicit) @brief get a pointer value (implicit)
Explicit pointer access to the internally stored JSON value. No copies are Implicit pointer access to the internally stored JSON value. No copies are
made. made.
@warning The pointer becomes invalid if the underlying JSON object @warning Writing data to the pointee of the result yields an undefined
changes. state.
@tparam PointerType pointer type; must be a pointer to @ref array_t, @ref @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, object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,
@ref number_unsigned_t, or @ref number_float_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 @return pointer to the internally stored JSON value if the requested
pointer type @a PointerType fits to the JSON value; `nullptr` otherwise pointer type @a PointerType fits to the JSON value; `nullptr` otherwise
...@@ -13717,45 +13732,43 @@ class basic_json ...@@ -13717,45 +13732,43 @@ class basic_json
@liveexample{The example below shows how pointers to internal values of a @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 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 `nullptr` is returned if the value and the requested pointer type does not
match.,get__PointerType} match.,get_ptr}
@sa @ref get_ptr() for explicit pointer-member access
@since version 1.0.0 @since version 1.0.0
*/ */
template<typename PointerType, typename std::enable_if< template<typename PointerType, typename std::enable_if<
std::is_pointer<PointerType>::value, int>::type = 0> std::is_pointer<PointerType>::value, int>::type = 0>
PointerType get() noexcept auto get_ptr() noexcept -> decltype(std::declval<basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))
{ {
// delegate the call to get_ptr // delegate the call to get_impl_ptr<>()
return get_ptr<PointerType>(); return get_impl_ptr(static_cast<PointerType>(nullptr));
} }
/*! /*!
@brief get a pointer value (explicit) @brief get a pointer value (implicit)
@copydoc get() @copydoc get_ptr()
*/ */
template<typename PointerType, typename std::enable_if< template<typename PointerType, typename std::enable_if<
std::is_pointer<PointerType>::value, int>::type = 0> std::is_pointer<PointerType>::value and
constexpr const PointerType get() const noexcept std::is_const<typename std::remove_pointer<PointerType>::type>::value, int>::type = 0>
constexpr auto get_ptr() const noexcept -> decltype(std::declval<const basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))
{ {
// delegate the call to get_ptr // delegate the call to get_impl_ptr<>() const
return get_ptr<PointerType>(); return get_impl_ptr(static_cast<PointerType>(nullptr));
} }
/*! /*!
@brief get a pointer value (implicit) @brief get a pointer value (explicit)
Implicit pointer access to the internally stored JSON value. No copies are Explicit pointer access to the internally stored JSON value. No copies are
made. made.
@warning Writing data to the pointee of the result yields an undefined @warning The pointer becomes invalid if the underlying JSON object
state. changes.
@tparam PointerType pointer type; must be a pointer to @ref array_t, @ref @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, 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 @ref number_unsigned_t, or @ref number_float_t.
assertion.
@return pointer to the internally stored JSON value if the requested @return pointer to the internally stored JSON value if the requested
pointer type @a PointerType fits to the JSON value; `nullptr` otherwise pointer type @a PointerType fits to the JSON value; `nullptr` otherwise
...@@ -13765,59 +13778,30 @@ class basic_json ...@@ -13765,59 +13778,30 @@ class basic_json
@liveexample{The example below shows how pointers to internal values of a @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 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 `nullptr` is returned if the value and the requested pointer type does not
match.,get_ptr} match.,get__PointerType}
@sa @ref get_ptr() for explicit pointer-member access
@since version 1.0.0 @since version 1.0.0
*/ */
template<typename PointerType, typename std::enable_if< template<typename PointerType, typename std::enable_if<
std::is_pointer<PointerType>::value, int>::type = 0> std::is_pointer<PointerType>::value, int>::type = 0>
PointerType get_ptr() noexcept auto get() noexcept -> decltype(std::declval<basic_json_t&>().template get_ptr<PointerType>())
{ {
// get the type of the PointerType (remove pointer and const) // delegate the call to get_ptr
using pointee_t = typename std::remove_const<typename return get_ptr<PointerType>();
std::remove_pointer<typename
std::remove_const<PointerType>::type>::type>::type;
// make sure the type matches the allowed types
static_assert(
std::is_same<object_t, pointee_t>::value
or std::is_same<array_t, pointee_t>::value
or std::is_same<string_t, pointee_t>::value
or std::is_same<boolean_t, pointee_t>::value
or std::is_same<number_integer_t, pointee_t>::value
or std::is_same<number_unsigned_t, pointee_t>::value
or std::is_same<number_float_t, pointee_t>::value
, "incompatible pointer type");
// delegate the call to get_impl_ptr<>()
return get_impl_ptr(static_cast<PointerType>(nullptr));
} }
/*! /*!
@brief get a pointer value (implicit) @brief get a pointer value (explicit)
@copydoc get_ptr() @copydoc get()
*/ */
template<typename PointerType, typename std::enable_if< template<typename PointerType, typename std::enable_if<
std::is_pointer<PointerType>::value and std::is_pointer<PointerType>::value, int>::type = 0>
std::is_const<typename std::remove_pointer<PointerType>::type>::value, int>::type = 0> constexpr auto get() const noexcept -> decltype(std::declval<const basic_json_t&>().template get_ptr<PointerType>())
constexpr const PointerType get_ptr() const noexcept
{ {
// get the type of the PointerType (remove pointer and const) // delegate the call to get_ptr
using pointee_t = typename std::remove_const<typename return get_ptr<PointerType>();
std::remove_pointer<typename
std::remove_const<PointerType>::type>::type>::type;
// make sure the type matches the allowed types
static_assert(
std::is_same<object_t, pointee_t>::value
or std::is_same<array_t, pointee_t>::value
or std::is_same<string_t, pointee_t>::value
or std::is_same<boolean_t, pointee_t>::value
or std::is_same<number_integer_t, pointee_t>::value
or std::is_same<number_unsigned_t, pointee_t>::value
or std::is_same<number_float_t, pointee_t>::value
, "incompatible pointer type");
// delegate the call to get_impl_ptr<>() const
return get_impl_ptr(static_cast<PointerType>(nullptr));
} }
/*! /*!
...@@ -13901,12 +13885,14 @@ class basic_json ...@@ -13901,12 +13885,14 @@ class basic_json
not std::is_same<ValueType, detail::json_ref<basic_json>>::value and not std::is_same<ValueType, detail::json_ref<basic_json>>::value and
not std::is_same<ValueType, typename string_t::value_type>::value and not std::is_same<ValueType, typename string_t::value_type>::value and
not detail::is_basic_json<ValueType>::value not detail::is_basic_json<ValueType>::value
#ifndef _MSC_VER // fix for issue #167 operator<< ambiguity under VS2015 #ifndef _MSC_VER // fix for issue #167 operator<< ambiguity under VS2015
and not std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>::value and not std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>::value
#if defined(JSON_HAS_CPP_17) && defined(_MSC_VER) and _MSC_VER <= 1914 #if defined(JSON_HAS_CPP_17) && defined(_MSC_VER) and _MSC_VER <= 1914
and not std::is_same<ValueType, typename std::string_view>::value and not std::is_same<ValueType, typename std::string_view>::value
#endif #endif
#endif #endif
and detail::is_detected<detail::get_template_function, const basic_json_t&, ValueType>::value
, int >::type = 0 > , int >::type = 0 >
operator ValueType() const operator ValueType() const
{ {
......
...@@ -811,3 +811,9 @@ TEST_CASE("Issue #924") ...@@ -811,3 +811,9 @@ TEST_CASE("Issue #924")
CHECK_NOTHROW(j.get<Evil>()); CHECK_NOTHROW(j.get<Evil>());
CHECK_NOTHROW(j.get<std::vector<Evil>>()); CHECK_NOTHROW(j.get<std::vector<Evil>>());
} }
TEST_CASE("Issue #1237")
{
struct non_convertible_type {};
static_assert(not std::is_convertible<json, non_convertible_type>::value, "");
}
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