From 7384a9adb4816cd344b93d5801580428559e7324 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20DELRIEU?= Date: Wed, 12 Sep 2018 17:01:27 +0200 Subject: [PATCH] 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. --- include/nlohmann/detail/meta/type_traits.hpp | 24 +++- include/nlohmann/json.hpp | 104 ++++++---------- single_include/nlohmann/json.hpp | 124 ++++++++----------- test/src/unit-udt.cpp | 6 + 4 files changed, 118 insertions(+), 140 deletions(-) diff --git a/include/nlohmann/detail/meta/type_traits.hpp b/include/nlohmann/detail/meta/type_traits.hpp index b358de659e..bb932ef60c 100644 --- a/include/nlohmann/detail/meta/type_traits.hpp +++ b/include/nlohmann/detail/meta/type_traits.hpp @@ -65,6 +65,9 @@ using to_json_function = decltype(T::to_json(std::declval()...)); template using from_json_function = decltype(T::from_json(std::declval()...)); +template +using get_template_function = decltype(std::declval().template get()); + /////////////////// // is_ functions // /////////////////// @@ -185,8 +188,12 @@ struct is_compatible_integer_type CompatibleNumberIntegerType> {}; // trait checking if JSONSerializer::from_json(json const&, udt&) exists -template -struct has_from_json +template +struct has_from_json : std::false_type {}; + +template +struct has_from_json::value>> { using serializer = typename BasicJsonType::template json_serializer; @@ -197,8 +204,11 @@ struct has_from_json // This trait checks if JSONSerializer::from_json(json const&) exists // this overload is used for non-default-constructible user-defined-types +template +struct has_non_default_from_json : std::false_type {}; + template -struct has_non_default_from_json +struct has_non_default_from_json::value>> { using serializer = typename BasicJsonType::template json_serializer; @@ -208,8 +218,12 @@ struct has_non_default_from_json }; // This trait checks if BasicJsonType::json_serializer::to_json exists -template -struct has_to_json +// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion. +template +struct has_to_json : std::false_type {}; + +template +struct has_to_json::value>> { using serializer = typename BasicJsonType::template json_serializer; diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index ee78c1c160..53b6997627 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -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 @@ -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::value, int>::type = 0> - PointerType get() noexcept + auto get_ptr() noexcept -> decltype(std::declval().get_impl_ptr(std::declval())) { - // delegate the call to get_ptr - return get_ptr(); + // delegate the call to get_impl_ptr<>() + return get_impl_ptr(static_cast(nullptr)); } /*! - @brief get a pointer value (explicit) - @copydoc get() + @brief get a pointer value (implicit) + @copydoc get_ptr() */ template::value, int>::type = 0> - constexpr const PointerType get() const noexcept + std::is_pointer::value and + std::is_const::type>::value, int>::type = 0> + constexpr auto get_ptr() const noexcept -> decltype(std::declval().get_impl_ptr(std::declval())) { - // delegate the call to get_ptr - return get_ptr(); + // delegate the call to get_impl_ptr<>() const + return get_impl_ptr(static_cast(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 @@ -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::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::type>::type>::type; - // make sure the type matches the allowed types - static_assert( - std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - , "incompatible pointer type"); - - // delegate the call to get_impl_ptr<>() - return get_impl_ptr(static_cast(nullptr)); + auto get() noexcept -> decltype(std::declval().get_ptr()) + { + // delegate the call to get_ptr + return get_ptr(); } /*! - @brief get a pointer value (implicit) - @copydoc get_ptr() + @brief get a pointer value (explicit) + @copydoc get() */ template::value and - std::is_const::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::type>::type>::type; - // make sure the type matches the allowed types - static_assert( - std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - , "incompatible pointer type"); - - // delegate the call to get_impl_ptr<>() const - return get_impl_ptr(static_cast(nullptr)); + std::is_pointer::value, int>::type = 0> + constexpr auto get() const noexcept -> decltype(std::declval().get_ptr()) + { + // delegate the call to get_ptr + return get_ptr(); } /*! @@ -2828,12 +2798,14 @@ class basic_json not std::is_same>::value and not std::is_same::value and not detail::is_basic_json::value + #ifndef _MSC_VER // fix for issue #167 operator<< ambiguity under VS2015 and not std::is_same>::value #if defined(JSON_HAS_CPP_17) && defined(_MSC_VER) and _MSC_VER <= 1914 and not std::is_same::value #endif #endif + and detail::is_detected::value , int >::type = 0 > operator ValueType() const { diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 69e4bddcf3..66fb993c98 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -425,6 +425,9 @@ using to_json_function = decltype(T::to_json(std::declval()...)); template using from_json_function = decltype(T::from_json(std::declval()...)); +template +using get_template_function = decltype(std::declval().template get()); + /////////////////// // is_ functions // /////////////////// @@ -545,8 +548,12 @@ struct is_compatible_integer_type CompatibleNumberIntegerType> {}; // trait checking if JSONSerializer::from_json(json const&, udt&) exists -template -struct has_from_json +template +struct has_from_json : std::false_type {}; + +template +struct has_from_json::value>> { using serializer = typename BasicJsonType::template json_serializer; @@ -557,8 +564,11 @@ struct has_from_json // This trait checks if JSONSerializer::from_json(json const&) exists // this overload is used for non-default-constructible user-defined-types +template +struct has_non_default_from_json : std::false_type {}; + template -struct has_non_default_from_json +struct has_non_default_from_json::value>> { using serializer = typename BasicJsonType::template json_serializer; @@ -568,8 +578,12 @@ struct has_non_default_from_json }; // This trait checks if BasicJsonType::json_serializer::to_json exists -template -struct has_to_json +// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion. +template +struct has_to_json : std::false_type {}; + +template +struct has_to_json::value>> { using serializer = typename BasicJsonType::template json_serializer; @@ -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. - @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 @@ -13717,45 +13732,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::value, int>::type = 0> - PointerType get() noexcept + auto get_ptr() noexcept -> decltype(std::declval().get_impl_ptr(std::declval())) { - // delegate the call to get_ptr - return get_ptr(); + // delegate the call to get_impl_ptr<>() + return get_impl_ptr(static_cast(nullptr)); } /*! - @brief get a pointer value (explicit) - @copydoc get() + @brief get a pointer value (implicit) + @copydoc get_ptr() */ template::value, int>::type = 0> - constexpr const PointerType get() const noexcept + std::is_pointer::value and + std::is_const::type>::value, int>::type = 0> + constexpr auto get_ptr() const noexcept -> decltype(std::declval().get_impl_ptr(std::declval())) { - // delegate the call to get_ptr - return get_ptr(); + // delegate the call to get_impl_ptr<>() const + return get_impl_ptr(static_cast(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 @@ -13765,59 +13778,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::value, int>::type = 0> - PointerType get_ptr() noexcept + auto get() noexcept -> decltype(std::declval().get_ptr()) { - // get the type of the PointerType (remove pointer and const) - using pointee_t = typename std::remove_const::type>::type>::type; - // make sure the type matches the allowed types - static_assert( - std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - , "incompatible pointer type"); - - // delegate the call to get_impl_ptr<>() - return get_impl_ptr(static_cast(nullptr)); + // delegate the call to get_ptr + return get_ptr(); } /*! - @brief get a pointer value (implicit) - @copydoc get_ptr() + @brief get a pointer value (explicit) + @copydoc get() */ template::value and - std::is_const::type>::value, int>::type = 0> - constexpr const PointerType get_ptr() const noexcept + std::is_pointer::value, int>::type = 0> + constexpr auto get() const noexcept -> decltype(std::declval().get_ptr()) { - // get the type of the PointerType (remove pointer and const) - using pointee_t = typename std::remove_const::type>::type>::type; - // make sure the type matches the allowed types - static_assert( - std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - , "incompatible pointer type"); - - // delegate the call to get_impl_ptr<>() const - return get_impl_ptr(static_cast(nullptr)); + // delegate the call to get_ptr + return get_ptr(); } /*! @@ -13901,12 +13885,14 @@ class basic_json not std::is_same>::value and not std::is_same::value and not detail::is_basic_json::value + #ifndef _MSC_VER // fix for issue #167 operator<< ambiguity under VS2015 and not std::is_same>::value #if defined(JSON_HAS_CPP_17) && defined(_MSC_VER) and _MSC_VER <= 1914 and not std::is_same::value #endif #endif + and detail::is_detected::value , int >::type = 0 > operator ValueType() const { diff --git a/test/src/unit-udt.cpp b/test/src/unit-udt.cpp index f59999ee17..a289305648 100644 --- a/test/src/unit-udt.cpp +++ b/test/src/unit-udt.cpp @@ -811,3 +811,9 @@ TEST_CASE("Issue #924") CHECK_NOTHROW(j.get()); CHECK_NOTHROW(j.get>()); } + +TEST_CASE("Issue #1237") +{ + struct non_convertible_type {}; + static_assert(not std::is_convertible::value, ""); +}