* 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.
This commit is contained in:
Théo DELRIEU 2018-09-12 17:01:27 +02:00
parent e4bc98d036
commit 4e52277b70
No known key found for this signature in database
GPG key ID: A5A505438C20539A
4 changed files with 146 additions and 168 deletions

View file

@ -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
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, typename = void>
struct has_from_json : std::false_type {};
template <typename BasicJsonType, typename T> template <typename BasicJsonType, typename T>
struct has_from_json 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
// 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
}; };
// This trait checks if BasicJsonType::json_serializer<T>::to_json exists // 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> template <typename BasicJsonType, typename T>
struct has_to_json 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>;

View file

@ -2623,6 +2623,53 @@ class basic_json
return JSONSerializer<ValueTypeCV>::from_json(*this); return JSONSerializer<ValueTypeCV>::from_json(*this);
} }
/*!
@brief get a pointer value (implicit)
Implicit pointer access to the internally stored JSON value. No copies are
made.
@warning Writing data to the pointee of the result yields an undefined
state.
@tparam PointerType pointer type; must be a pointer to @ref array_t, @ref
object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,
@ref number_unsigned_t, or @ref number_float_t. Enforced by a static
assertion.
@return pointer to the internally stored JSON value if the requested
pointer type @a PointerType fits to the JSON value; `nullptr` otherwise
@complexity Constant.
@liveexample{The example below shows how pointers to internal values of a
JSON value can be requested. Note that no type conversions are made and a
`nullptr` is returned if the value and the requested pointer type does not
match.,get_ptr}
@since version 1.0.0
*/
template<typename PointerType, typename std::enable_if<
std::is_pointer<PointerType>::value, int>::type = 0>
auto get_ptr() noexcept -> decltype(std::declval<basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))
{
// delegate the call to get_impl_ptr<>()
return get_impl_ptr(static_cast<PointerType>(nullptr));
}
/*!
@brief get a pointer value (implicit)
@copydoc get_ptr()
*/
template<typename PointerType, typename std::enable_if<
std::is_pointer<PointerType>::value and
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_impl_ptr<>() const
return get_impl_ptr(static_cast<PointerType>(nullptr));
}
/*! /*!
@brief get a pointer value (explicit) @brief get a pointer value (explicit)
@ -2652,7 +2699,7 @@ class basic_json
*/ */
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() noexcept -> decltype(std::declval<basic_json_t&>().template get_ptr<PointerType>())
{ {
// delegate the call to get_ptr // delegate the call to get_ptr
return get_ptr<PointerType>(); return get_ptr<PointerType>();
@ -2664,89 +2711,12 @@ class basic_json
*/ */
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>
constexpr const PointerType get() const noexcept constexpr auto get() const noexcept -> decltype(std::declval<const basic_json_t&>().template get_ptr<PointerType>())
{ {
// delegate the call to get_ptr // delegate the call to get_ptr
return get_ptr<PointerType>(); return get_ptr<PointerType>();
} }
/*!
@brief get a pointer value (implicit)
Implicit pointer access to the internally stored JSON value. No copies are
made.
@warning Writing data to the pointee of the result yields an undefined
state.
@tparam PointerType pointer type; must be a pointer to @ref array_t, @ref
object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,
@ref number_unsigned_t, or @ref number_float_t. Enforced by a static
assertion.
@return pointer to the internally stored JSON value if the requested
pointer type @a PointerType fits to the JSON value; `nullptr` otherwise
@complexity Constant.
@liveexample{The example below shows how pointers to internal values of a
JSON value can be requested. Note that no type conversions are made and a
`nullptr` is returned if the value and the requested pointer type does not
match.,get_ptr}
@since version 1.0.0
*/
template<typename PointerType, typename std::enable_if<
std::is_pointer<PointerType>::value, int>::type = 0>
PointerType get_ptr() noexcept
{
// get the type of the PointerType (remove pointer and const)
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<>()
return get_impl_ptr(static_cast<PointerType>(nullptr));
}
/*!
@brief get a pointer value (implicit)
@copydoc get_ptr()
*/
template<typename PointerType, typename std::enable_if<
std::is_pointer<PointerType>::value and
std::is_const<typename std::remove_pointer<PointerType>::type>::value, int>::type = 0>
constexpr const PointerType get_ptr() const noexcept
{
// get the type of the PointerType (remove pointer and const)
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));
}
/*! /*!
@brief get a reference value (implicit) @brief get a reference value (implicit)
@ -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
{ {

View file

@ -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
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, typename = void>
struct has_from_json : std::false_type {};
template <typename BasicJsonType, typename T> template <typename BasicJsonType, typename T>
struct has_from_json 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
// 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
}; };
// This trait checks if BasicJsonType::json_serializer<T>::to_json exists // 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> template <typename BasicJsonType, typename T>
struct has_to_json 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>;
@ -13696,6 +13710,53 @@ class basic_json
return JSONSerializer<ValueTypeCV>::from_json(*this); return JSONSerializer<ValueTypeCV>::from_json(*this);
} }
/*!
@brief get a pointer value (implicit)
Implicit pointer access to the internally stored JSON value. No copies are
made.
@warning Writing data to the pointee of the result yields an undefined
state.
@tparam PointerType pointer type; must be a pointer to @ref array_t, @ref
object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,
@ref number_unsigned_t, or @ref number_float_t. Enforced by a static
assertion.
@return pointer to the internally stored JSON value if the requested
pointer type @a PointerType fits to the JSON value; `nullptr` otherwise
@complexity Constant.
@liveexample{The example below shows how pointers to internal values of a
JSON value can be requested. Note that no type conversions are made and a
`nullptr` is returned if the value and the requested pointer type does not
match.,get_ptr}
@since version 1.0.0
*/
template<typename PointerType, typename std::enable_if<
std::is_pointer<PointerType>::value, int>::type = 0>
auto get_ptr() noexcept -> decltype(std::declval<basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))
{
// delegate the call to get_impl_ptr<>()
return get_impl_ptr(static_cast<PointerType>(nullptr));
}
/*!
@brief get a pointer value (implicit)
@copydoc get_ptr()
*/
template<typename PointerType, typename std::enable_if<
std::is_pointer<PointerType>::value and
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_impl_ptr<>() const
return get_impl_ptr(static_cast<PointerType>(nullptr));
}
/*! /*!
@brief get a pointer value (explicit) @brief get a pointer value (explicit)
@ -13725,7 +13786,7 @@ class basic_json
*/ */
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() noexcept -> decltype(std::declval<basic_json_t&>().template get_ptr<PointerType>())
{ {
// delegate the call to get_ptr // delegate the call to get_ptr
return get_ptr<PointerType>(); return get_ptr<PointerType>();
@ -13737,89 +13798,12 @@ class basic_json
*/ */
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>
constexpr const PointerType get() const noexcept constexpr auto get() const noexcept -> decltype(std::declval<const basic_json_t&>().template get_ptr<PointerType>())
{ {
// delegate the call to get_ptr // delegate the call to get_ptr
return get_ptr<PointerType>(); return get_ptr<PointerType>();
} }
/*!
@brief get a pointer value (implicit)
Implicit pointer access to the internally stored JSON value. No copies are
made.
@warning Writing data to the pointee of the result yields an undefined
state.
@tparam PointerType pointer type; must be a pointer to @ref array_t, @ref
object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,
@ref number_unsigned_t, or @ref number_float_t. Enforced by a static
assertion.
@return pointer to the internally stored JSON value if the requested
pointer type @a PointerType fits to the JSON value; `nullptr` otherwise
@complexity Constant.
@liveexample{The example below shows how pointers to internal values of a
JSON value can be requested. Note that no type conversions are made and a
`nullptr` is returned if the value and the requested pointer type does not
match.,get_ptr}
@since version 1.0.0
*/
template<typename PointerType, typename std::enable_if<
std::is_pointer<PointerType>::value, int>::type = 0>
PointerType get_ptr() noexcept
{
// get the type of the PointerType (remove pointer and const)
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<>()
return get_impl_ptr(static_cast<PointerType>(nullptr));
}
/*!
@brief get a pointer value (implicit)
@copydoc get_ptr()
*/
template<typename PointerType, typename std::enable_if<
std::is_pointer<PointerType>::value and
std::is_const<typename std::remove_pointer<PointerType>::type>::value, int>::type = 0>
constexpr const PointerType get_ptr() const noexcept
{
// get the type of the PointerType (remove pointer and const)
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));
}
/*! /*!
@brief get a reference value (implicit) @brief get a reference value (implicit)
@ -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
{ {

View file

@ -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, "");
}