From 4e52277b70999ccf8858c7995dd72808a7e82c33 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 | 130 ++++++---------- single_include/nlohmann/json.hpp | 154 +++++++++---------- test/src/unit-udt.cpp | 6 + 4 files changed, 146 insertions(+), 168 deletions(-) diff --git a/include/nlohmann/detail/meta/type_traits.hpp b/include/nlohmann/detail/meta/type_traits.hpp index b358de659..bb932ef60 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 9e1ed0024..dfd441a7b 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -2623,6 +2623,53 @@ class basic_json return JSONSerializer::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::value, int>::type = 0> + auto get_ptr() noexcept -> decltype(std::declval().get_impl_ptr(std::declval())) + { + // delegate the call to get_impl_ptr<>() + return get_impl_ptr(static_cast(nullptr)); + } + + /*! + @brief get a pointer value (implicit) + @copydoc get_ptr() + */ + template::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_impl_ptr<>() const + return get_impl_ptr(static_cast(nullptr)); + } + /*! @brief get a pointer value (explicit) @@ -2652,7 +2699,7 @@ class basic_json */ template::value, int>::type = 0> - PointerType get() noexcept + auto get() noexcept -> decltype(std::declval().template get_ptr()) { // delegate the call to get_ptr return get_ptr(); @@ -2664,89 +2711,12 @@ class basic_json */ template::value, int>::type = 0> - constexpr const PointerType get() const noexcept + constexpr auto get() const noexcept -> decltype(std::declval().template get_ptr()) { // delegate the call to get_ptr return get_ptr(); } - /*! - @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::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)); - } - - /*! - @brief get a pointer value (implicit) - @copydoc get_ptr() - */ - 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)); - } - /*! @brief get a reference value (implicit) @@ -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 3d7967bb6..a6405870d 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; @@ -13696,6 +13710,53 @@ class basic_json return JSONSerializer::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::value, int>::type = 0> + auto get_ptr() noexcept -> decltype(std::declval().get_impl_ptr(std::declval())) + { + // delegate the call to get_impl_ptr<>() + return get_impl_ptr(static_cast(nullptr)); + } + + /*! + @brief get a pointer value (implicit) + @copydoc get_ptr() + */ + template::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_impl_ptr<>() const + return get_impl_ptr(static_cast(nullptr)); + } + /*! @brief get a pointer value (explicit) @@ -13725,7 +13786,7 @@ class basic_json */ template::value, int>::type = 0> - PointerType get() noexcept + auto get() noexcept -> decltype(std::declval().template get_ptr()) { // delegate the call to get_ptr return get_ptr(); @@ -13737,89 +13798,12 @@ class basic_json */ template::value, int>::type = 0> - constexpr const PointerType get() const noexcept + constexpr auto get() const noexcept -> decltype(std::declval().template get_ptr()) { // delegate the call to get_ptr return get_ptr(); } - /*! - @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::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)); - } - - /*! - @brief get a pointer value (implicit) - @copydoc get_ptr() - */ - 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)); - } - /*! @brief get a reference value (implicit) @@ -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 f59999ee1..a28930564 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, ""); +}