Skip to content

Commit

Permalink
Fix issue #1237
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
theodelrieu committed Sep 13, 2018
1 parent 186c747 commit db5e00a
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 140 deletions.
24 changes: 19 additions & 5 deletions include/nlohmann/detail/meta/type_traits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ using to_json_function = decltype(T::to_json(std::declval<Args>()...));
template <typename T, typename... 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 //
///////////////////
Expand Down Expand Up @@ -185,8 +188,12 @@ struct is_compatible_integer_type
CompatibleNumberIntegerType> {};

// trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists
template<typename BasicJsonType, typename T>
struct has_from_json
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>;

Expand All @@ -197,8 +204,11 @@ struct has_from_json

// 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
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>;

Expand All @@ -208,8 +218,12 @@ struct has_non_default_from_json
};

// This trait checks if BasicJsonType::json_serializer<T>::to_json exists
template<typename BasicJsonType, typename T>
struct has_to_json
// 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>;

Expand Down
104 changes: 38 additions & 66 deletions include/nlohmann/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
@warning The pointer becomes invalid if the underlying JSON object
changes.
@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.
@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
Expand All @@ -2644,45 +2645,43 @@ class basic_json
@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__PointerType}
@sa @ref get_ptr() for explicit pointer-member access
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() noexcept
auto get_ptr() noexcept -> decltype(std::declval<basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))
{
// delegate the call to get_ptr
return get_ptr<PointerType>();
// delegate the call to get_impl_ptr<>()
return get_impl_ptr(static_cast<PointerType>(nullptr));
}

/*!
@brief get a pointer value (explicit)
@copydoc get()
@brief get a pointer value (implicit)
@copydoc get_ptr()
*/
template<typename PointerType, typename std::enable_if<
std::is_pointer<PointerType>::value, int>::type = 0>
constexpr const PointerType get() const noexcept
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_ptr
return get_ptr<PointerType>();
// delegate the call to get_impl_ptr<>() const
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.
@warning Writing data to the pointee of the result yields an undefined
state.
@warning The pointer becomes invalid if the underlying JSON object
changes.
@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.
@ref number_unsigned_t, or @ref number_float_t.
@return pointer to the internally stored JSON value if the requested
pointer type @a PointerType fits to the JSON value; `nullptr` otherwise
Expand All @@ -2692,59 +2691,30 @@ class basic_json
@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}
match.,get__PointerType}
@sa @ref get_ptr() for explicit pointer-member access
@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));
auto get() noexcept -> decltype(std::declval<basic_json_t&>().template get_ptr<PointerType>())
{
// delegate the call to get_ptr
return get_ptr<PointerType>();
}

/*!
@brief get a pointer value (implicit)
@copydoc get_ptr()
@brief get a pointer value (explicit)
@copydoc get()
*/
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));
std::is_pointer<PointerType>::value, int>::type = 0>
constexpr auto get() const noexcept -> decltype(std::declval<const basic_json_t&>().template get_ptr<PointerType>())
{
// delegate the call to get_ptr
return get_ptr<PointerType>();
}

/*!
Expand Down Expand Up @@ -2828,12 +2798,14 @@ class basic_json
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 detail::is_basic_json<ValueType>::value

#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
#if defined(JSON_HAS_CPP_17) && defined(_MSC_VER) and _MSC_VER <= 1914
and not std::is_same<ValueType, typename std::string_view>::value
#endif
#endif
and detail::is_detected<detail::get_template_function, const basic_json_t&, ValueType>::value
, int >::type = 0 >
operator ValueType() const
{
Expand Down
Loading

0 comments on commit db5e00a

Please sign in to comment.