From dcee778c1e72c9b08bea2b7b099d7e5c78393409 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20DELRIEU?= <theo.delrieu@tanker.io> Date: Wed, 17 Jan 2018 12:17:38 +0100 Subject: [PATCH] fix sfinae on basic_json UDT constructor Avoid compiler errors when performing SFINAE checks on basic_json and incomplete types. --- develop/detail/meta.hpp | 26 ++++++++++++++++++++++++- develop/json.hpp | 16 +++++++--------- src/json.hpp | 42 +++++++++++++++++++++++++++++++---------- test/src/unit-udt.cpp | 19 +++++++++++++++++++ 4 files changed, 83 insertions(+), 20 deletions(-) diff --git a/develop/detail/meta.hpp b/develop/detail/meta.hpp index bea42a07..585b8d44 100644 --- a/develop/detail/meta.hpp +++ b/develop/detail/meta.hpp @@ -96,6 +96,14 @@ template<> struct priority_tag<0> {}; // has_/is_ functions // //////////////////////// +// source: https://stackoverflow.com/a/37193089/4116453 + +template <typename T, typename = void> +struct is_complete_type : std::false_type {}; + +template <typename T> +struct is_complete_type<T, decltype(void(sizeof(T)))> : std::true_type {}; + NLOHMANN_JSON_HAS_HELPER(mapped_type); NLOHMANN_JSON_HAS_HELPER(key_type); NLOHMANN_JSON_HAS_HELPER(value_type); @@ -171,7 +179,6 @@ struct is_compatible_integer_type RealIntegerType, CompatibleNumberIntegerType > ::value; }; - // trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists template<typename BasicJsonType, typename T> struct has_from_json @@ -221,6 +228,23 @@ struct has_to_json std::declval<typename BasicJsonType::template json_serializer<T, void>>()))>::value; }; +template <typename BasicJsonType, typename CompatibleCompleteType> +struct is_compatible_complete_type +{ + static constexpr bool value = + not std::is_base_of<std::istream, CompatibleCompleteType>::value and + not std::is_same<BasicJsonType, CompatibleCompleteType>::value and + not is_basic_json_nested_type<BasicJsonType, CompatibleCompleteType>::value and + has_to_json<BasicJsonType, CompatibleCompleteType>::value; +}; + +template <typename BasicJsonType, typename CompatibleType> +struct is_compatible_type + : conjunction<is_complete_type<CompatibleType>, + is_compatible_complete_type<BasicJsonType, CompatibleType>> +{ +}; + // taken from ranges-v3 template<typename T> struct static_const diff --git a/develop/json.hpp b/develop/json.hpp index f4c139e4..26a2b111 100644 --- a/develop/json.hpp +++ b/develop/json.hpp @@ -1551,15 +1551,13 @@ class basic_json @since version 2.1.0 */ - template<typename CompatibleType, typename U = detail::uncvref_t<CompatibleType>, - detail::enable_if_t<not std::is_base_of<std::istream, U>::value and - not std::is_same<U, basic_json_t>::value and - not detail::is_basic_json_nested_type< - basic_json_t, U>::value and - detail::has_to_json<basic_json, U>::value, - int> = 0> - basic_json(CompatibleType && val) noexcept(noexcept(JSONSerializer<U>::to_json( - std::declval<basic_json_t&>(), std::forward<CompatibleType>(val)))) + template <typename CompatibleType, + typename U = detail::uncvref_t<CompatibleType>, + detail::enable_if_t< + detail::is_compatible_type<basic_json_t, U>::value, int> = 0> + basic_json(CompatibleType && val) noexcept(noexcept( + JSONSerializer<U>::to_json(std::declval<basic_json_t&>(), + std::forward<CompatibleType>(val)))) { JSONSerializer<U>::to_json(*this, std::forward<CompatibleType>(val)); assert_invariant(); diff --git a/src/json.hpp b/src/json.hpp index a80cc36a..d95ae8db 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -312,6 +312,14 @@ template<> struct priority_tag<0> {}; // has_/is_ functions // //////////////////////// +// source: https://stackoverflow.com/a/37193089/4116453 + +template <typename T, typename = void> +struct is_complete_type : std::false_type {}; + +template <typename T> +struct is_complete_type<T, decltype(void(sizeof(T)))> : std::true_type {}; + NLOHMANN_JSON_HAS_HELPER(mapped_type); NLOHMANN_JSON_HAS_HELPER(key_type); NLOHMANN_JSON_HAS_HELPER(value_type); @@ -387,7 +395,6 @@ struct is_compatible_integer_type RealIntegerType, CompatibleNumberIntegerType > ::value; }; - // trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists template<typename BasicJsonType, typename T> struct has_from_json @@ -437,6 +444,23 @@ struct has_to_json std::declval<typename BasicJsonType::template json_serializer<T, void>>()))>::value; }; +template <typename BasicJsonType, typename CompatibleCompleteType> +struct is_compatible_complete_type +{ + static constexpr bool value = + not std::is_base_of<std::istream, CompatibleCompleteType>::value and + not std::is_same<BasicJsonType, CompatibleCompleteType>::value and + not is_basic_json_nested_type<BasicJsonType, CompatibleCompleteType>::value and + has_to_json<BasicJsonType, CompatibleCompleteType>::value; +}; + +template <typename BasicJsonType, typename CompatibleType> +struct is_compatible_type + : conjunction<is_complete_type<CompatibleType>, + is_compatible_complete_type<BasicJsonType, CompatibleType>> +{ +}; + // taken from ranges-v3 template<typename T> struct static_const @@ -8839,15 +8863,13 @@ class basic_json @since version 2.1.0 */ - template<typename CompatibleType, typename U = detail::uncvref_t<CompatibleType>, - detail::enable_if_t<not std::is_base_of<std::istream, U>::value and - not std::is_same<U, basic_json_t>::value and - not detail::is_basic_json_nested_type< - basic_json_t, U>::value and - detail::has_to_json<basic_json, U>::value, - int> = 0> - basic_json(CompatibleType && val) noexcept(noexcept(JSONSerializer<U>::to_json( - std::declval<basic_json_t&>(), std::forward<CompatibleType>(val)))) + template <typename CompatibleType, + typename U = detail::uncvref_t<CompatibleType>, + detail::enable_if_t< + detail::is_compatible_type<basic_json_t, U>::value, int> = 0> + basic_json(CompatibleType && val) noexcept(noexcept( + JSONSerializer<U>::to_json(std::declval<basic_json_t&>(), + std::forward<CompatibleType>(val)))) { JSONSerializer<U>::to_json(*this, std::forward<CompatibleType>(val)); assert_invariant(); diff --git a/test/src/unit-udt.cpp b/test/src/unit-udt.cpp index 4fd6f7ec..a4adc3be 100644 --- a/test/src/unit-udt.cpp +++ b/test/src/unit-udt.cpp @@ -692,3 +692,22 @@ TEST_CASE("custom serializer that does adl by default", "[udt]") CHECK(me == j.get<udt::person>()); CHECK(me == cj.get<udt::person>()); } + +namespace +{ +struct incomplete; + +// std::is_constructible is broken on macOS' libc++ +// use the cppreference implementation + +template <typename T, typename = void> +struct is_constructible_patched : std::false_type {}; + +template <typename T> +struct is_constructible_patched<T, decltype(void(json(std::declval<T>())))> : std::true_type {}; +} + +TEST_CASE("an incomplete type does not trigger a compiler error in non-evaluated context", "[udt]") +{ + static_assert(not is_constructible_patched<json, incomplete>::value, ""); +}