From 27d0dfc17ad3cc59e7977d2282943481102becd5 Mon Sep 17 00:00:00 2001 From: Anthony Van Herrewege Date: Wed, 30 Oct 2019 14:54:52 +0100 Subject: [PATCH 1/4] Fix #1647: non-member operator== breaks enum (de)serialization. --- include/nlohmann/json.hpp | 6 +++--- single_include/nlohmann/json.hpp | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index d63ffb92..a1958a34 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -2633,11 +2633,11 @@ class basic_json detail::has_non_default_from_json::value, int> = 0> ValueType get() const noexcept(noexcept( - JSONSerializer::from_json(std::declval()))) + JSONSerializer::from_json(std::declval()))) { static_assert(not std::is_reference::value, "get() cannot be used with reference types, you might want to use get_ref()"); - return JSONSerializer::from_json(*this); + return JSONSerializer::from_json(*this); } /*! @@ -8062,7 +8062,7 @@ struct hash /// @note: do not remove the space after '<', /// see https://github.com/nlohmann/json/pull/679 template<> -struct less< ::nlohmann::detail::value_t> +struct less<::nlohmann::detail::value_t> { /*! @brief compare two value_t enum values diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 3094de39..2c588ce5 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -17163,11 +17163,11 @@ class basic_json detail::has_non_default_from_json::value, int> = 0> ValueType get() const noexcept(noexcept( - JSONSerializer::from_json(std::declval()))) + JSONSerializer::from_json(std::declval()))) { static_assert(not std::is_reference::value, "get() cannot be used with reference types, you might want to use get_ref()"); - return JSONSerializer::from_json(*this); + return JSONSerializer::from_json(*this); } /*! From fb9a2643c8354b4e436c305378e53a1f6b591a66 Mon Sep 17 00:00:00 2001 From: Anthony Van Herrewege Date: Wed, 30 Oct 2019 15:46:31 +0100 Subject: [PATCH 2/4] Add test for #1647. --- test/src/unit-conversions.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/test/src/unit-conversions.cpp b/test/src/unit-conversions.cpp index 9d48e29d..1a5033df 100644 --- a/test/src/unit-conversions.cpp +++ b/test/src/unit-conversions.cpp @@ -1537,6 +1537,31 @@ NLOHMANN_JSON_SERIALIZE_ENUM(TaskState, {TS_COMPLETED, "completed"}, }) +namespace +{ +// Helper struct to test whether compile error does not trigger upon +// conversion of an enum in the presence of non-member operator== for +// user-defined type with "non default" from_json function (#1647). +struct NonDefaultFromJsonStruct { }; + +inline bool operator== (NonDefaultFromJsonStruct const& lhs, NonDefaultFromJsonStruct const& rhs) +{ + return true; +} +} + +namespace nlohmann +{ +template <> +struct adl_serializer +{ + static NonDefaultFromJsonStruct from_json (json const& j) + { + return {}; + } +}; +} + TEST_CASE("JSON to enum mapping") { SECTION("enum class") From ddda67a09642c765f3d0898c3093a0804bb5a308 Mon Sep 17 00:00:00 2001 From: Anthony Van Herrewege Date: Wed, 30 Oct 2019 16:16:34 +0100 Subject: [PATCH 3/4] Don't capture json input by value (fixed #1822). --- include/nlohmann/detail/macro_scope.hpp | 48 ++++++++++++------------- single_include/nlohmann/json.hpp | 48 ++++++++++++------------- 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index 2be7581d..27dddc6b 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -78,30 +78,30 @@ @def NLOHMANN_JSON_SERIALIZE_ENUM @since version 3.4.0 */ -#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ - template \ - inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ - { \ - static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ - static const std::pair m[] = __VA_ARGS__; \ - auto it = std::find_if(std::begin(m), std::end(m), \ - [e](const std::pair& ej_pair) -> bool \ - { \ - return ej_pair.first == e; \ - }); \ - j = ((it != std::end(m)) ? it : std::begin(m))->second; \ - } \ - template \ - inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ - { \ - static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ - static const std::pair m[] = __VA_ARGS__; \ - auto it = std::find_if(std::begin(m), std::end(m), \ - [j](const std::pair& ej_pair) -> bool \ - { \ - return ej_pair.second == j; \ - }); \ - e = ((it != std::end(m)) ? it : std::begin(m))->first; \ +#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ + template \ + inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [e](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.first == e; \ + }); \ + j = ((it != std::end(m)) ? it : std::begin(m))->second; \ + } \ + template \ + inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [&j](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.second == j; \ + }); \ + e = ((it != std::end(m)) ? it : std::begin(m))->first; \ } // Ugly macros to avoid uglier copy-paste when specializing basic_json. They diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 2c588ce5..18e3541c 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -1782,30 +1782,30 @@ JSON_HEDLEY_DIAGNOSTIC_POP @def NLOHMANN_JSON_SERIALIZE_ENUM @since version 3.4.0 */ -#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ - template \ - inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ - { \ - static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ - static const std::pair m[] = __VA_ARGS__; \ - auto it = std::find_if(std::begin(m), std::end(m), \ - [e](const std::pair& ej_pair) -> bool \ - { \ - return ej_pair.first == e; \ - }); \ - j = ((it != std::end(m)) ? it : std::begin(m))->second; \ - } \ - template \ - inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ - { \ - static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ - static const std::pair m[] = __VA_ARGS__; \ - auto it = std::find_if(std::begin(m), std::end(m), \ - [j](const std::pair& ej_pair) -> bool \ - { \ - return ej_pair.second == j; \ - }); \ - e = ((it != std::end(m)) ? it : std::begin(m))->first; \ +#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ + template \ + inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [e](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.first == e; \ + }); \ + j = ((it != std::end(m)) ? it : std::begin(m))->second; \ + } \ + template \ + inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [&j](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.second == j; \ + }); \ + e = ((it != std::end(m)) ? it : std::begin(m))->first; \ } // Ugly macros to avoid uglier copy-paste when specializing basic_json. They From ec9647ae63532501fd2e50a70cf54b5210ae8c45 Mon Sep 17 00:00:00 2001 From: Anthony VH Date: Mon, 4 Nov 2019 20:45:24 +0100 Subject: [PATCH 4/4] Moved test for #1647 regression to regressions file. --- test/src/unit-conversions.cpp | 25 ----------------------- test/src/unit-regression.cpp | 38 +++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 25 deletions(-) diff --git a/test/src/unit-conversions.cpp b/test/src/unit-conversions.cpp index 1a5033df..9d48e29d 100644 --- a/test/src/unit-conversions.cpp +++ b/test/src/unit-conversions.cpp @@ -1537,31 +1537,6 @@ NLOHMANN_JSON_SERIALIZE_ENUM(TaskState, {TS_COMPLETED, "completed"}, }) -namespace -{ -// Helper struct to test whether compile error does not trigger upon -// conversion of an enum in the presence of non-member operator== for -// user-defined type with "non default" from_json function (#1647). -struct NonDefaultFromJsonStruct { }; - -inline bool operator== (NonDefaultFromJsonStruct const& lhs, NonDefaultFromJsonStruct const& rhs) -{ - return true; -} -} - -namespace nlohmann -{ -template <> -struct adl_serializer -{ - static NonDefaultFromJsonStruct from_json (json const& j) - { - return {}; - } -}; -} - TEST_CASE("JSON to enum mapping") { SECTION("enum class") diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp index 0c75dcbc..869d6cc8 100644 --- a/test/src/unit-regression.cpp +++ b/test/src/unit-regression.cpp @@ -159,6 +159,38 @@ bool operator==(Data const& lhs, Data const& rhs) using float_json = nlohmann::basic_json; +///////////////////////////////////////////////////////////////////// +// for #1647 +///////////////////////////////////////////////////////////////////// +namespace +{ +struct NonDefaultFromJsonStruct { }; + +inline bool operator== (NonDefaultFromJsonStruct const& lhs, NonDefaultFromJsonStruct const& rhs) +{ + return true; +} + +enum class for_1647 { one, two }; + +NLOHMANN_JSON_SERIALIZE_ENUM(for_1647, +{ + {for_1647::one, "one"}, + {for_1647::two, "two"}, +}) +} + +namespace nlohmann +{ +template <> +struct adl_serializer +{ + static NonDefaultFromJsonStruct from_json (json const& j) + { + return {}; + } +}; +} TEST_CASE("regression tests") { @@ -1820,6 +1852,12 @@ TEST_CASE("regression tests") CHECK(j.contains(jptr1)); CHECK(j.contains(jptr2)); } + + SECTION("issue #1647 - compile error when deserializing enum if both non-default from_json and non-member operator== exists for other type") + { + auto val = nlohmann::json("one").get(); + CHECK(val == for_1647::one); + } } #if not defined(JSON_NOEXCEPTION)