From f6f169e7f1bd09539c903d39f97078aa14c76209 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 23 Nov 2019 00:21:33 +0100 Subject: [PATCH 01/11] :construction: conversions for std::optional --- .../nlohmann/detail/conversions/from_json.hpp | 23 +++++++ .../nlohmann/detail/conversions/to_json.hpp | 24 +++++++ single_include/nlohmann/json.hpp | 47 +++++++++++++ tests/src/unit-conversions.cpp | 67 +++++++++++++++++++ 4 files changed, 161 insertions(+) diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index c6299aa0b2..f7c972d5a3 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -20,6 +20,14 @@ #include // pair, declval #include // valarray +#ifdef JSON_HAS_CPP_17 + #if __has_include() + #include + #elif __has_include() + #include + #endif +#endif + #include #include #include @@ -43,6 +51,21 @@ inline void from_json(const BasicJsonType& j, typename std::nullptr_t& n) n = nullptr; } +#ifdef JSON_HAS_CPP_17 +template +void from_json(const BasicJsonType& j, std::optional& opt) +{ + if (j.is_null()) + { + opt = std::nullopt; + } + else + { + opt = j.template get(); + } +} +#endif + // overloads for basic_json template parameters template < typename BasicJsonType, typename ArithmeticType, enable_if_t < std::is_arithmetic::value&& diff --git a/include/nlohmann/detail/conversions/to_json.hpp b/include/nlohmann/detail/conversions/to_json.hpp index b33d726b48..c14a1eb5eb 100644 --- a/include/nlohmann/detail/conversions/to_json.hpp +++ b/include/nlohmann/detail/conversions/to_json.hpp @@ -24,6 +24,14 @@ #include #include +#ifdef JSON_HAS_CPP_17 + #if __has_include() + #include + #elif __has_include() + #include + #endif +#endif + NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail { @@ -260,6 +268,22 @@ struct external_constructor // to_json // ///////////// +#ifdef JSON_HAS_CPP_17 +template::value, int> = 0> +void to_json(BasicJsonType& j, const std::optional& opt) +{ + if (opt.has_value()) + { + j = *opt; + } + else + { + j = nullptr; + } +} +#endif + template::value, int> = 0> inline void to_json(BasicJsonType& j, T b) noexcept diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 2448bf22d3..4dfaa2bcc0 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -169,6 +169,14 @@ #include // pair, declval #include // valarray +#ifdef JSON_HAS_CPP_17 + #if __has_include() + #include + #elif __has_include() + #include + #endif +#endif + // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ @@ -4603,6 +4611,21 @@ inline void from_json(const BasicJsonType& j, typename std::nullptr_t& n) n = nullptr; } +#ifdef JSON_HAS_CPP_17 +template +void from_json(const BasicJsonType& j, std::optional& opt) +{ + if (j.is_null()) + { + opt = std::nullopt; + } + else + { + opt = j.template get(); + } +} +#endif + // overloads for basic_json template parameters template < typename BasicJsonType, typename ArithmeticType, enable_if_t < std::is_arithmetic::value&& @@ -5334,6 +5357,14 @@ class tuple_element> // #include +#ifdef JSON_HAS_CPP_17 + #if __has_include() + #include + #elif __has_include() + #include + #endif +#endif + NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail { @@ -5570,6 +5601,22 @@ struct external_constructor // to_json // ///////////// +#ifdef JSON_HAS_CPP_17 +template::value, int> = 0> +void to_json(BasicJsonType& j, const std::optional& opt) +{ + if (opt.has_value()) + { + j = *opt; + } + else + { + j = nullptr; + } +} +#endif + template::value, int> = 0> inline void to_json(BasicJsonType& j, T b) noexcept diff --git a/tests/src/unit-conversions.cpp b/tests/src/unit-conversions.cpp index 9c6431238c..49b6ccc418 100644 --- a/tests/src/unit-conversions.cpp +++ b/tests/src/unit-conversions.cpp @@ -32,6 +32,14 @@ using nlohmann::json; DOCTEST_CLANG_SUPPRESS_WARNING_PUSH DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors") +#ifdef JSON_HAS_CPP_17 + #if __has_include() + #include + #elif __has_include() + #include + #endif +#endif + TEST_CASE("value conversion") { SECTION("get an object (explicit)") @@ -1569,4 +1577,63 @@ TEST_CASE("JSON to enum mapping") } } +#ifdef JSON_HAS_CPP_17 +TEST_CASE("std::optional") +{ + SECTION("null") + { + json j_null; + std::optional opt_null; + + CHECK(json(opt_null) == j_null); + CHECK(std::optional(j_null) == std::nullopt); + } + + SECTION("string") + { + json j_string = "string"; + std::optional opt_string = "string"; + + CHECK(json(opt_string) == j_string); + CHECK(std::optional(j_string) == opt_string); + } + + SECTION("bool") + { + json j_bool = true; + std::optional opt_bool = true; + + CHECK(json(opt_bool) == j_bool); + CHECK(std::optional(j_bool) == opt_bool); + } + + SECTION("number") + { + json j_number = 1; + std::optional opt_int = 1; + + CHECK(json(opt_int) == j_number); + CHECK(std::optional(j_number) == opt_int); + } + + SECTION("array") + { + json j_array = {1, 2, nullptr}; + std::vector> opt_array = {{1, 2, std::nullopt}}; + + CHECK(json(opt_array) == j_array); + CHECK(std::vector>(j_array) == opt_array); + } + + SECTION("object") + { + json j_object = {{"one", 1}, {"two", 2}, {"zero", nullptr}}; + std::map> opt_object {{"one", 1}, {"two", 2}, {"zero", std::nullopt}}; + + CHECK(json(opt_object) == j_object); + CHECK(std::map>(j_object) == opt_object); + } +} +#endif + DOCTEST_CLANG_SUPPRESS_WARNING_POP From 57f0f502cbb0c118a8adeb93330c63cdf4d2bc61 Mon Sep 17 00:00:00 2001 From: Alexander Karzhenkov Date: Sat, 28 Aug 2021 21:31:45 +0500 Subject: [PATCH 02/11] Class template nlohmann::optional --- .../nlohmann/detail/conversions/from_json.hpp | 22 +- .../nlohmann/detail/conversions/to_json.hpp | 11 +- include/nlohmann/optional.hpp | 173 +++++++++++++++ single_include/nlohmann/json.hpp | 207 ++++++++++++++++-- tests/src/unit-conversions.cpp | 105 ++++++++- tests/src/unit-optional.cpp | 153 +++++++++++++ 6 files changed, 628 insertions(+), 43 deletions(-) create mode 100644 include/nlohmann/optional.hpp create mode 100644 tests/src/unit-optional.cpp diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index f7c972d5a3..c7a37ad65a 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -20,14 +20,6 @@ #include // pair, declval #include // valarray -#ifdef JSON_HAS_CPP_17 - #if __has_include() - #include - #elif __has_include() - #include - #endif -#endif - #include #include #include @@ -36,6 +28,7 @@ #include #include #include +#include NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail @@ -64,6 +57,19 @@ void from_json(const BasicJsonType& j, std::optional& opt) opt = j.template get(); } } + +template +void from_json(const BasicJsonType& j, nlohmann::optional& opt) +{ + if (j.is_null()) + { + opt = std::nullopt; + } + else + { + opt = j.template get(); + } +} #endif // overloads for basic_json template parameters diff --git a/include/nlohmann/detail/conversions/to_json.hpp b/include/nlohmann/detail/conversions/to_json.hpp index c14a1eb5eb..3e66c8ac20 100644 --- a/include/nlohmann/detail/conversions/to_json.hpp +++ b/include/nlohmann/detail/conversions/to_json.hpp @@ -23,14 +23,7 @@ #include #include #include - -#ifdef JSON_HAS_CPP_17 - #if __has_include() - #include - #elif __has_include() - #include - #endif -#endif +#include NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail @@ -271,7 +264,7 @@ struct external_constructor #ifdef JSON_HAS_CPP_17 template::value, int> = 0> -void to_json(BasicJsonType& j, const std::optional& opt) +void to_json(BasicJsonType& j, const std::optional& opt) noexcept { if (opt.has_value()) { diff --git a/include/nlohmann/optional.hpp b/include/nlohmann/optional.hpp new file mode 100644 index 0000000000..29b844c7e2 --- /dev/null +++ b/include/nlohmann/optional.hpp @@ -0,0 +1,173 @@ +#pragma once + +#include + +#ifdef JSON_HAS_CPP_17 + +#include +#include + +namespace nlohmann +{ + +template +class optional : public std::optional +{ + // *INDENT-OFF* + + using base_type = std::optional; + + template + struct has_conversion_operator : std::false_type { }; + + template + struct has_conversion_operator().operator optional())> : std::true_type { }; + + template + using is_base_constructible_from = std::is_constructible; + + template + using is_convertible_to_base = std::is_convertible; + + template + using enable_int_if = std::enable_if_t; + + template + using use_conversion_operator = + enable_int_if< + has_conversion_operator + >; + + template + using use_implicit_forwarding = + enable_int_if< + std::conjunction< + std::negation>, + is_base_constructible_from, + is_convertible_to_base + > + >; + + template + using use_explicit_forwarding = + enable_int_if< + std::conjunction< + std::negation>, + is_base_constructible_from, + std::negation> + > + >; + + template + using can_construct_in_place_from = + enable_int_if< + is_base_constructible_from + >; + + struct noexcept_fix_t {}; + + public: + + const base_type& base() const + { + return *this; + } + + constexpr optional() noexcept(noexcept(std::optional())) = default; + + constexpr optional(std::nullopt_t /* unused */) noexcept + : base_type(std::nullopt) + { + } + + template = 0> + constexpr optional(U&& value) + noexcept(noexcept( + base_type(std::forward(value).operator optional()) + )) : + base_type(std::forward(value).operator optional()) + { + } + + template = 0> + constexpr optional(U&& value) + noexcept(noexcept( + base_type(std::forward(value)) + )) : + base_type(std::forward(value)) + { + } + + template = 0> + explicit + constexpr optional(U&& value) + noexcept(noexcept( + base_type(std::forward(value)) + )) : + base_type(std::forward(value)) + { + } + + template = 0> + explicit + constexpr optional(std::in_place_t /* unused */, U&& u, Args&&... args) + noexcept(noexcept( + base_type(std::in_place, std::forward(u), std::forward(args)...) + )) : + base_type(std::in_place, std::forward(u), std::forward(args)...) + { + } + + template &, Args...> = 0> + explicit + constexpr optional(std::in_place_t /* unused */, std::initializer_list u, Args&&... args) + noexcept(noexcept( + base_type(std::in_place, u, std::forward(args)...) + )) : + base_type(std::in_place, u, std::forward(args)...) + { + } + + // *INDENT-ON* +}; + +template +constexpr bool operator == (const optional& lhs, const optional& rhs) +{ + return lhs.base() == rhs.base(); +} + +template +constexpr bool operator != (const optional& lhs, const optional& rhs) +{ + return lhs.base() != rhs.base(); +} + +template +constexpr bool operator < (const optional& lhs, const optional& rhs) +{ + return lhs.base() < rhs.base(); +} + +template +constexpr bool operator <= (const optional& lhs, const optional& rhs) +{ + return lhs.base() <= rhs.base(); +} + +template +constexpr bool operator > (const optional& lhs, const optional& rhs) +{ + return lhs.base() > rhs.base(); +} + +template +constexpr bool operator >= (const optional& lhs, const optional& rhs) +{ + return lhs.base() >= rhs.base(); +} + +} // namespace nlohmann + +#endif // JSON_HAS_CPP_17 diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 4dfaa2bcc0..dbd5e1a6f1 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -169,14 +169,6 @@ #include // pair, declval #include // valarray -#ifdef JSON_HAS_CPP_17 - #if __has_include() - #include - #elif __has_include() - #include - #endif -#endif - // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ @@ -4596,6 +4588,182 @@ NLOHMANN_JSON_NAMESPACE_END // #include +// #include + + +// #include + + +#ifdef JSON_HAS_CPP_17 + +#include +#include + +namespace nlohmann +{ + +template +class optional : public std::optional +{ + // *INDENT-OFF* + + using base_type = std::optional; + + template + struct has_conversion_operator : std::false_type { }; + + template + struct has_conversion_operator().operator optional())> : std::true_type { }; + + template + using is_base_constructible_from = std::is_constructible; + + template + using is_convertible_to_base = std::is_convertible; + + template + using enable_int_if = std::enable_if_t; + + template + using use_conversion_operator = + enable_int_if< + has_conversion_operator + >; + + template + using use_implicit_forwarding = + enable_int_if< + std::conjunction< + std::negation>, + is_base_constructible_from, + is_convertible_to_base + > + >; + + template + using use_explicit_forwarding = + enable_int_if< + std::conjunction< + std::negation>, + is_base_constructible_from, + std::negation> + > + >; + + template + using can_construct_in_place_from = + enable_int_if< + is_base_constructible_from + >; + + struct noexcept_fix_t {}; + + public: + + const base_type& base() const + { + return *this; + } + + constexpr optional() noexcept(noexcept(std::optional())) = default; + + constexpr optional(std::nullopt_t /* unused */) noexcept + : base_type(std::nullopt) + { + } + + template = 0> + constexpr optional(U&& value) + noexcept(noexcept( + base_type(std::forward(value).operator optional()) + )) : + base_type(std::forward(value).operator optional()) + { + } + + template = 0> + constexpr optional(U&& value) + noexcept(noexcept( + base_type(std::forward(value)) + )) : + base_type(std::forward(value)) + { + } + + template = 0> + explicit + constexpr optional(U&& value) + noexcept(noexcept( + base_type(std::forward(value)) + )) : + base_type(std::forward(value)) + { + } + + template = 0> + explicit + constexpr optional(std::in_place_t /* unused */, U&& u, Args&&... args) + noexcept(noexcept( + base_type(std::in_place, std::forward(u), std::forward(args)...) + )) : + base_type(std::in_place, std::forward(u), std::forward(args)...) + { + } + + template &, Args...> = 0> + explicit + constexpr optional(std::in_place_t /* unused */, std::initializer_list u, Args&&... args) + noexcept(noexcept( + base_type(std::in_place, u, std::forward(args)...) + )) : + base_type(std::in_place, u, std::forward(args)...) + { + } + + // *INDENT-ON* +}; + +template +constexpr bool operator == (const optional& lhs, const optional& rhs) +{ + return lhs.base() == rhs.base(); +} + +template +constexpr bool operator != (const optional& lhs, const optional& rhs) +{ + return lhs.base() != rhs.base(); +} + +template +constexpr bool operator < (const optional& lhs, const optional& rhs) +{ + return lhs.base() < rhs.base(); +} + +template +constexpr bool operator <= (const optional& lhs, const optional& rhs) +{ + return lhs.base() <= rhs.base(); +} + +template +constexpr bool operator > (const optional& lhs, const optional& rhs) +{ + return lhs.base() > rhs.base(); +} + +template +constexpr bool operator >= (const optional& lhs, const optional& rhs) +{ + return lhs.base() >= rhs.base(); +} + +} // namespace nlohmann + +#endif // JSON_HAS_CPP_17 + NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail @@ -4624,6 +4792,19 @@ void from_json(const BasicJsonType& j, std::optional& opt) opt = j.template get(); } } + +template +void from_json(const BasicJsonType& j, nlohmann::optional& opt) +{ + if (j.is_null()) + { + opt = std::nullopt; + } + else + { + opt = j.template get(); + } +} #endif // overloads for basic_json template parameters @@ -5356,14 +5537,8 @@ class tuple_element> // #include +// #include -#ifdef JSON_HAS_CPP_17 - #if __has_include() - #include - #elif __has_include() - #include - #endif -#endif NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail @@ -5604,7 +5779,7 @@ struct external_constructor #ifdef JSON_HAS_CPP_17 template::value, int> = 0> -void to_json(BasicJsonType& j, const std::optional& opt) +void to_json(BasicJsonType& j, const std::optional& opt) noexcept { if (opt.has_value()) { diff --git a/tests/src/unit-conversions.cpp b/tests/src/unit-conversions.cpp index 49b6ccc418..72ef03de21 100644 --- a/tests/src/unit-conversions.cpp +++ b/tests/src/unit-conversions.cpp @@ -31,14 +31,15 @@ using nlohmann::json; // NLOHMANN_JSON_SERIALIZE_ENUM uses a static std::pair DOCTEST_CLANG_SUPPRESS_WARNING_PUSH DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-macros") -#ifdef JSON_HAS_CPP_17 - #if __has_include() - #include - #elif __has_include() - #include - #endif -#endif +// For testing copy-initialization of std::optional and nlohmann::optional +// (clang doesn't need the suppressing) +#define SUPPRESS_CONVERSION_WARNING \ + DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wconversion") + +#define RESTORE_CONVERSION_WARNING \ + DOCTEST_GCC_SUPPRESS_WARNING_POP TEST_CASE("value conversion") { @@ -1586,7 +1587,22 @@ TEST_CASE("std::optional") std::optional opt_null; CHECK(json(opt_null) == j_null); - CHECK(std::optional(j_null) == std::nullopt); + CHECK(j_null.get>() == std::nullopt); + + using opt_int = std::optional; + + auto opt1 = []() -> opt_int { return json().get(); }; + auto opt2 = []() -> opt_int { return opt_int(json()); }; // NOLINT(modernize-return-braced-init-list) + + CHECK(opt1() == std::nullopt); + CHECK_THROWS_AS(opt2(), json::type_error&); + +#if JSON_USE_IMPLICIT_CONVERSIONS + SUPPRESS_CONVERSION_WARNING + auto opt3 = []() -> opt_int { return json(); }; + RESTORE_CONVERSION_WARNING + CHECK_THROWS_AS(opt3(), json::type_error&); +#endif } SECTION("string") @@ -1622,7 +1638,7 @@ TEST_CASE("std::optional") std::vector> opt_array = {{1, 2, std::nullopt}}; CHECK(json(opt_array) == j_array); - CHECK(std::vector>(j_array) == opt_array); + CHECK(j_array.get>>() == opt_array); } SECTION("object") @@ -1631,7 +1647,76 @@ TEST_CASE("std::optional") std::map> opt_object {{"one", 1}, {"two", 2}, {"zero", std::nullopt}}; CHECK(json(opt_object) == j_object); - CHECK(std::map>(j_object) == opt_object); + CHECK(j_object.get>>() == opt_object); + } +} + +TEST_CASE("nlohmann::optional") +{ + SECTION("null") + { + json j_null; + nlohmann::optional opt_null; + + CHECK(json(opt_null) == j_null); + CHECK(j_null.get>() == std::nullopt); + + using opt_int = nlohmann::optional; + + auto opt1 = []() -> opt_int { return json().get(); }; + auto opt2 = []() -> opt_int { return opt_int(json()); }; // NOLINT(modernize-return-braced-init-list) + SUPPRESS_CONVERSION_WARNING + auto opt3 = []() -> opt_int { return json(); }; + RESTORE_CONVERSION_WARNING + + CHECK(opt1() == std::nullopt); + CHECK(opt2() == std::nullopt); + CHECK(opt3() == std::nullopt); + } + + SECTION("string") + { + json j_string = "string"; + nlohmann::optional opt_string = "string"; + + CHECK(json(opt_string) == j_string); + CHECK(nlohmann::optional(j_string) == opt_string); + } + + SECTION("bool") + { + json j_bool = true; + nlohmann::optional opt_bool = true; + + CHECK(json(opt_bool) == j_bool); + CHECK(nlohmann::optional(j_bool) == opt_bool); + } + + SECTION("number") + { + json j_number = 1; + nlohmann::optional opt_int = 1; + + CHECK(json(opt_int) == j_number); + CHECK(nlohmann::optional(j_number) == opt_int); + } + + SECTION("array") + { + json j_array = {1, 2, nullptr}; + std::vector> opt_array = {{1, 2, std::nullopt}}; + + CHECK(json(opt_array) == j_array); + CHECK(j_array.get>>() == opt_array); + } + + SECTION("object") + { + json j_object = {{"one", 1}, {"two", 2}, {"zero", nullptr}}; + std::map> opt_object{{"one", 1}, {"two", 2}, {"zero", std::nullopt}}; + + CHECK(json(opt_object) == j_object); + CHECK(j_object.get>>() == opt_object); } } #endif diff --git a/tests/src/unit-optional.cpp b/tests/src/unit-optional.cpp new file mode 100644 index 0000000000..53a7f87986 --- /dev/null +++ b/tests/src/unit-optional.cpp @@ -0,0 +1,153 @@ +#include "doctest_compatibility.h" + +#include + +#ifdef JSON_HAS_CPP_17 + +DOCTEST_GCC_SUPPRESS_WARNING_PUSH +DOCTEST_GCC_SUPPRESS_WARNING("-Wuseless-cast") + +#include +#include +#include + +using std::nullopt; +using std::in_place; +using nlohmann::optional; + +using opt_int = optional; +using opt_vec = optional>; +using opt_ptr = optional>; + +using std_opt_int = std::optional; +using std_opt_vec = std::optional>; +using std_opt_ptr = std::optional>; + +// NOLINTBEGIN(bugprone-unchecked-optional-access,bugprone-use-after-move,hicpp-invalid-access-moved,clang-analyzer-cplusplus.Move) + +TEST_CASE("nlohmann::optional comparison") +{ + CHECK(opt_int() == nullopt); + CHECK(nullopt == opt_int()); + CHECK(opt_int() == opt_int(nullopt)); + CHECK(opt_int(0) == opt_int(0)); + CHECK_FALSE(opt_int(0) == nullopt); + CHECK_FALSE(nullopt == opt_int(0)); + CHECK_FALSE(opt_int(0) == opt_int(1)); + + CHECK(opt_int(0) != nullopt); + CHECK(nullopt != opt_int(0)); + CHECK(opt_int(0) != opt_int(1)); + CHECK_FALSE(opt_int() != nullopt); + CHECK_FALSE(nullopt != opt_int()); + CHECK_FALSE(opt_int() != opt_int(nullopt)); + CHECK_FALSE(opt_int(0) != opt_int(0)); + + CHECK(opt_int(0) > nullopt); + CHECK(opt_int(1) > opt_int(0)); + CHECK_FALSE(nullopt > opt_int(0)); + CHECK_FALSE(opt_int(0) > opt_int(1)); + + CHECK(opt_int(0) >= nullopt); + CHECK(opt_int(1) >= opt_int(0)); + CHECK_FALSE(nullopt >= opt_int(0)); + CHECK_FALSE(opt_int(0) >= opt_int(1)); + + CHECK(nullopt < opt_int(0)); + CHECK(opt_int(0) < opt_int(1)); + CHECK_FALSE(opt_int(0) < nullopt); + CHECK_FALSE(opt_int(1) < opt_int(0)); + + CHECK(nullopt <= opt_int(0)); + CHECK(opt_int(0) <= opt_int(1)); + CHECK_FALSE(opt_int(0) <= nullopt); + CHECK_FALSE(opt_int(1) <= opt_int(0)); +} + +TEST_CASE("nlohmann::optional constructors") +{ + struct S1 + { + operator int() noexcept + { + return 0; + } + }; + struct S2 : S1 + { + operator opt_int() noexcept + { + return nullopt; + } + }; + + CHECK(opt_int(S1()) == opt_int(0)); + CHECK(opt_int(S2()) == nullopt); + + CHECK(opt_int(S1()) == std_opt_int(S1())); + CHECK(opt_int(S2()) != std_opt_int(S2())); + + CHECK(opt_int(std_opt_int(0)) == opt_int(0)); + CHECK(std_opt_int(opt_int(0)) == opt_int(0)); + + CHECK(opt_int(in_place) == std_opt_int(in_place)); + CHECK(opt_vec(in_place) == std_opt_vec(in_place)); + CHECK(opt_ptr(in_place) == std_opt_ptr(in_place)); + + CHECK(opt_vec(in_place, 5)->size() == 5); + CHECK(opt_vec(in_place, {1, 2, 3}) == std_opt_vec(in_place, {1, 2, 3})); + CHECK(**opt_ptr(in_place, new int{42}) == **std_opt_ptr(in_place, new int{42})); + + std::vector vec{1, 2, 3}; + CHECK(*opt_vec(in_place, vec.begin(), vec.end()) == vec); + + CHECK(opt_vec({1, 2, 3})->size() == 3); +} + +TEST_CASE("nlohmann::optional copy") +{ + opt_int opt1 = 111; + std_opt_int opt2 = 222; + + SECTION("1") + { + opt1 = std::as_const(opt2); + CHECK(*opt1 == 222); + CHECK(*opt_int(std::as_const(opt1)) == 222); + } + + SECTION("2") + { + opt2 = std::as_const(opt1); + CHECK(*opt2 == 111); + CHECK(*opt_int(std::as_const(opt2)) == 111); + } +} + +TEST_CASE("nlohmann::optional move") +{ + opt_ptr opt1(new int(111)); + std_opt_ptr opt2(new int(222)); + + SECTION("1") + { + opt1 = std::move(opt2); + CHECK(*opt2 == nullptr); + CHECK(**opt1 == 222); + CHECK(**opt_ptr(std::move(opt1)) == 222); + } + + SECTION("2") + { + opt2 = std::move(opt1); + CHECK(*opt1 == nullptr); + CHECK(**opt2 == 111); + CHECK(**opt_ptr(std::move(opt2)) == 111); + } +} + +// NOLINTEND(bugprone-unchecked-optional-access,bugprone-use-after-move,hicpp-invalid-access-moved,clang-analyzer-cplusplus.Move) + +DOCTEST_GCC_SUPPRESS_WARNING_POP + +#endif // JSON_HAS_CPP_17 From 385493410194a16a58a429224c016559019f8a3b Mon Sep 17 00:00:00 2001 From: Alexander Karzhenkov Date: Sun, 3 Jul 2022 13:15:35 +0500 Subject: [PATCH 03/11] Don't derive nlohmann::optional from std::optional --- .../nlohmann/detail/conversions/from_json.hpp | 2 +- .../nlohmann/detail/conversions/to_json.hpp | 7 + include/nlohmann/optional.hpp | 201 +++++++++++++++-- single_include/nlohmann/json.hpp | 210 +++++++++++++++--- tests/src/unit-optional.cpp | 4 +- 5 files changed, 372 insertions(+), 52 deletions(-) diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index c7a37ad65a..2c31e63313 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -59,7 +59,7 @@ void from_json(const BasicJsonType& j, std::optional& opt) } template -void from_json(const BasicJsonType& j, nlohmann::optional& opt) +void from_json(const BasicJsonType& j, optional& opt) { if (j.is_null()) { diff --git a/include/nlohmann/detail/conversions/to_json.hpp b/include/nlohmann/detail/conversions/to_json.hpp index 3e66c8ac20..5da41f0591 100644 --- a/include/nlohmann/detail/conversions/to_json.hpp +++ b/include/nlohmann/detail/conversions/to_json.hpp @@ -275,6 +275,13 @@ void to_json(BasicJsonType& j, const std::optional& opt) noexcept j = nullptr; } } + +template::value, int> = 0> +void to_json(BasicJsonType& j, const optional& opt) noexcept +{ + to_json(j, opt.base()); +} #endif template #include -namespace nlohmann -{ +NLOHMANN_JSON_NAMESPACE_BEGIN template -class optional : public std::optional +class optional { // *INDENT-OFF* using base_type = std::optional; + using value_type = T; template struct has_conversion_operator : std::false_type { }; @@ -67,17 +67,29 @@ class optional : public std::optional struct noexcept_fix_t {}; + base_type base_value; + public: - const base_type& base() const + base_type& base() & + { + return base_value; + } + + const base_type& base() const & { - return *this; + return base_value; + } + + base_type&& base() && + { + return std::move(base_value); } constexpr optional() noexcept(noexcept(std::optional())) = default; constexpr optional(std::nullopt_t /* unused */) noexcept - : base_type(std::nullopt) + : base_value(std::nullopt) { } @@ -86,7 +98,7 @@ class optional : public std::optional noexcept(noexcept( base_type(std::forward(value).operator optional()) )) : - base_type(std::forward(value).operator optional()) + base_value(std::forward(value).operator optional()) { } @@ -95,7 +107,7 @@ class optional : public std::optional noexcept(noexcept( base_type(std::forward(value)) )) : - base_type(std::forward(value)) + base_value(std::forward(value)) { } @@ -105,7 +117,7 @@ class optional : public std::optional noexcept(noexcept( base_type(std::forward(value)) )) : - base_type(std::forward(value)) + base_value(std::forward(value)) { } @@ -115,7 +127,7 @@ class optional : public std::optional noexcept(noexcept( base_type(std::in_place, std::forward(u), std::forward(args)...) )) : - base_type(std::in_place, std::forward(u), std::forward(args)...) + base_value(std::in_place, std::forward(u), std::forward(args)...) { } @@ -125,49 +137,190 @@ class optional : public std::optional noexcept(noexcept( base_type(std::in_place, u, std::forward(args)...) )) : - base_type(std::in_place, u, std::forward(args)...) + base_value(std::in_place, u, std::forward(args)...) { } + constexpr T& operator *() & noexcept { return *base_value; } + constexpr const T& operator *() const& noexcept { return *base_value; } + + constexpr T&& operator *() && noexcept { return static_cast(*base_value); } + constexpr const T&& operator *() const&& noexcept { return static_cast(*base_value); } + + constexpr T* operator ->() noexcept { return base_value.operator ->(); } + constexpr const T* operator ->() const noexcept { return base_value.operator ->(); } + + operator base_type& () & { return base_value; } + operator base_type&& () && { return std::move(base_value); } + // *INDENT-ON* }; +namespace detail::opt +{ + +template const T& cmp_val(const T& v) +{ + return v; +} +template const std::optional& cmp_val(const optional& v) +{ + return v.base(); +} +template void cmp_val(const std::optional& v) = delete; + +} // namespace detail::opt + +#ifdef JSON_HAS_CPP_20 + +template +auto operator == (const optional& lhs, const U& rhs) -> +decltype(detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs)) +{ + return detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs); +} + +// *INDENT-OFF* + +template +auto operator <=> (const optional& lhs, const U& rhs) -> +decltype(detail::opt::cmp_val(lhs) <=> detail::opt::cmp_val(rhs)) +{ + return detail::opt::cmp_val(lhs) <=> detail::opt::cmp_val(rhs); +} + +// *INDENT-ON* + +#else // JSON_HAS_CPP_20 + template -constexpr bool operator == (const optional& lhs, const optional& rhs) +constexpr auto operator == (const optional& lhs, const optional& rhs) -> +decltype(detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs)) +{ + return detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs); +} + +template +constexpr auto operator == (const optional& lhs, const U& rhs) -> +decltype(detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs)) +{ + return detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs); +} + +template +constexpr auto operator == (const T& lhs, const optional& rhs) -> +decltype(detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs)) { - return lhs.base() == rhs.base(); + return detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs); } template -constexpr bool operator != (const optional& lhs, const optional& rhs) +constexpr auto operator != (const optional& lhs, const optional& rhs) -> +decltype(detail::opt::cmp_val(lhs) != detail::opt::cmp_val(rhs)) +{ + return detail::opt::cmp_val(lhs) != detail::opt::cmp_val(rhs); +} + +template +constexpr auto operator != (const optional& lhs, const U& rhs) -> +decltype(detail::opt::cmp_val(lhs) != detail::opt::cmp_val(rhs)) +{ + return detail::opt::cmp_val(lhs) != detail::opt::cmp_val(rhs); +} + +template +constexpr auto operator != (const T& lhs, const optional& rhs) -> +decltype(detail::opt::cmp_val(lhs) != detail::opt::cmp_val(rhs)) { - return lhs.base() != rhs.base(); + return detail::opt::cmp_val(lhs) != detail::opt::cmp_val(rhs); } template -constexpr bool operator < (const optional& lhs, const optional& rhs) +constexpr auto operator < (const optional& lhs, const optional& rhs) -> +decltype(detail::opt::cmp_val(lhs) < detail::opt::cmp_val(rhs)) +{ + return detail::opt::cmp_val(lhs) < detail::opt::cmp_val(rhs); +} + +template +constexpr auto operator < (const optional& lhs, const U& rhs) -> +decltype(detail::opt::cmp_val(lhs) < detail::opt::cmp_val(rhs)) { - return lhs.base() < rhs.base(); + return detail::opt::cmp_val(lhs) < detail::opt::cmp_val(rhs); +} + +template +constexpr auto operator < (const T& lhs, const optional& rhs) -> +decltype(detail::opt::cmp_val(lhs) < detail::opt::cmp_val(rhs)) +{ + return detail::opt::cmp_val(lhs) < detail::opt::cmp_val(rhs); } template -constexpr bool operator <= (const optional& lhs, const optional& rhs) +constexpr auto operator <= (const optional& lhs, const optional& rhs) -> +decltype(detail::opt::cmp_val(lhs) <= detail::opt::cmp_val(rhs)) +{ + return detail::opt::cmp_val(lhs) <= detail::opt::cmp_val(rhs); +} + +template +constexpr auto operator <= (const optional& lhs, const U& rhs) -> +decltype(detail::opt::cmp_val(lhs) <= detail::opt::cmp_val(rhs)) +{ + return detail::opt::cmp_val(lhs) <= detail::opt::cmp_val(rhs); +} + +template +constexpr auto operator <= (const T& lhs, const optional& rhs) -> +decltype(detail::opt::cmp_val(lhs) <= detail::opt::cmp_val(rhs)) { - return lhs.base() <= rhs.base(); + return detail::opt::cmp_val(lhs) <= detail::opt::cmp_val(rhs); } template -constexpr bool operator > (const optional& lhs, const optional& rhs) +constexpr auto operator > (const optional& lhs, const optional& rhs) -> +decltype(detail::opt::cmp_val(lhs) > detail::opt::cmp_val(rhs)) +{ + return detail::opt::cmp_val(lhs) > detail::opt::cmp_val(rhs); +} + +template +constexpr auto operator > (const optional& lhs, const U& rhs) -> +decltype(detail::opt::cmp_val(lhs) > detail::opt::cmp_val(rhs)) +{ + return detail::opt::cmp_val(lhs) > detail::opt::cmp_val(rhs); +} + +template +constexpr auto operator > (const T& lhs, const optional& rhs) -> +decltype(detail::opt::cmp_val(lhs) > detail::opt::cmp_val(rhs)) { - return lhs.base() > rhs.base(); + return detail::opt::cmp_val(lhs) > detail::opt::cmp_val(rhs); } template -constexpr bool operator >= (const optional& lhs, const optional& rhs) +constexpr auto operator >= (const optional& lhs, const optional& rhs) -> +decltype(detail::opt::cmp_val(lhs) >= detail::opt::cmp_val(rhs)) +{ + return detail::opt::cmp_val(lhs) >= detail::opt::cmp_val(rhs); +} + +template +constexpr auto operator >= (const optional& lhs, const U& rhs) -> +decltype(detail::opt::cmp_val(lhs) >= detail::opt::cmp_val(rhs)) { - return lhs.base() >= rhs.base(); + return detail::opt::cmp_val(lhs) >= detail::opt::cmp_val(rhs); } -} // namespace nlohmann +template +constexpr auto operator >= (const T& lhs, const optional& rhs) -> +decltype(detail::opt::cmp_val(lhs) >= detail::opt::cmp_val(rhs)) +{ + return detail::opt::cmp_val(lhs) >= detail::opt::cmp_val(rhs); +} + +#endif // JSON_HAS_CPP_20 + +NLOHMANN_JSON_NAMESPACE_END #endif // JSON_HAS_CPP_17 diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index dbd5e1a6f1..85e718af9a 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -4599,15 +4599,15 @@ NLOHMANN_JSON_NAMESPACE_END #include #include -namespace nlohmann -{ +NLOHMANN_JSON_NAMESPACE_BEGIN template -class optional : public std::optional +class optional { // *INDENT-OFF* using base_type = std::optional; + using value_type = T; template struct has_conversion_operator : std::false_type { }; @@ -4659,17 +4659,29 @@ class optional : public std::optional struct noexcept_fix_t {}; + base_type base_value; + public: - const base_type& base() const + base_type& base() & { - return *this; + return base_value; + } + + const base_type& base() const & + { + return base_value; + } + + base_type&& base() && + { + return std::move(base_value); } constexpr optional() noexcept(noexcept(std::optional())) = default; constexpr optional(std::nullopt_t /* unused */) noexcept - : base_type(std::nullopt) + : base_value(std::nullopt) { } @@ -4678,7 +4690,7 @@ class optional : public std::optional noexcept(noexcept( base_type(std::forward(value).operator optional()) )) : - base_type(std::forward(value).operator optional()) + base_value(std::forward(value).operator optional()) { } @@ -4687,7 +4699,7 @@ class optional : public std::optional noexcept(noexcept( base_type(std::forward(value)) )) : - base_type(std::forward(value)) + base_value(std::forward(value)) { } @@ -4697,7 +4709,7 @@ class optional : public std::optional noexcept(noexcept( base_type(std::forward(value)) )) : - base_type(std::forward(value)) + base_value(std::forward(value)) { } @@ -4707,7 +4719,7 @@ class optional : public std::optional noexcept(noexcept( base_type(std::in_place, std::forward(u), std::forward(args)...) )) : - base_type(std::in_place, std::forward(u), std::forward(args)...) + base_value(std::in_place, std::forward(u), std::forward(args)...) { } @@ -4717,50 +4729,191 @@ class optional : public std::optional noexcept(noexcept( base_type(std::in_place, u, std::forward(args)...) )) : - base_type(std::in_place, u, std::forward(args)...) + base_value(std::in_place, u, std::forward(args)...) { } + constexpr T& operator *() & noexcept { return *base_value; } + constexpr const T& operator *() const& noexcept { return *base_value; } + + constexpr T&& operator *() && noexcept { return static_cast(*base_value); } + constexpr const T&& operator *() const&& noexcept { return static_cast(*base_value); } + + constexpr T* operator ->() noexcept { return base_value.operator ->(); } + constexpr const T* operator ->() const noexcept { return base_value.operator ->(); } + + operator base_type& () & { return base_value; } + operator base_type&& () && { return std::move(base_value); } + // *INDENT-ON* }; +namespace detail::opt +{ + +template const T& cmp_val(const T& v) +{ + return v; +} +template const std::optional& cmp_val(const optional& v) +{ + return v.base(); +} +template void cmp_val(const std::optional& v) = delete; + +} // namespace detail::opt + +#ifdef JSON_HAS_CPP_20 + +template +auto operator == (const optional& lhs, const U& rhs) -> +decltype(detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs)) +{ + return detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs); +} + +// *INDENT-OFF* + +template +auto operator <=> (const optional& lhs, const U& rhs) -> +decltype(detail::opt::cmp_val(lhs) <=> detail::opt::cmp_val(rhs)) +{ + return detail::opt::cmp_val(lhs) <=> detail::opt::cmp_val(rhs); +} + +// *INDENT-ON* + +#else // JSON_HAS_CPP_20 + template -constexpr bool operator == (const optional& lhs, const optional& rhs) +constexpr auto operator == (const optional& lhs, const optional& rhs) -> +decltype(detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs)) +{ + return detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs); +} + +template +constexpr auto operator == (const optional& lhs, const U& rhs) -> +decltype(detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs)) { - return lhs.base() == rhs.base(); + return detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs); +} + +template +constexpr auto operator == (const T& lhs, const optional& rhs) -> +decltype(detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs)) +{ + return detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs); } template -constexpr bool operator != (const optional& lhs, const optional& rhs) +constexpr auto operator != (const optional& lhs, const optional& rhs) -> +decltype(detail::opt::cmp_val(lhs) != detail::opt::cmp_val(rhs)) +{ + return detail::opt::cmp_val(lhs) != detail::opt::cmp_val(rhs); +} + +template +constexpr auto operator != (const optional& lhs, const U& rhs) -> +decltype(detail::opt::cmp_val(lhs) != detail::opt::cmp_val(rhs)) { - return lhs.base() != rhs.base(); + return detail::opt::cmp_val(lhs) != detail::opt::cmp_val(rhs); +} + +template +constexpr auto operator != (const T& lhs, const optional& rhs) -> +decltype(detail::opt::cmp_val(lhs) != detail::opt::cmp_val(rhs)) +{ + return detail::opt::cmp_val(lhs) != detail::opt::cmp_val(rhs); } template -constexpr bool operator < (const optional& lhs, const optional& rhs) +constexpr auto operator < (const optional& lhs, const optional& rhs) -> +decltype(detail::opt::cmp_val(lhs) < detail::opt::cmp_val(rhs)) +{ + return detail::opt::cmp_val(lhs) < detail::opt::cmp_val(rhs); +} + +template +constexpr auto operator < (const optional& lhs, const U& rhs) -> +decltype(detail::opt::cmp_val(lhs) < detail::opt::cmp_val(rhs)) +{ + return detail::opt::cmp_val(lhs) < detail::opt::cmp_val(rhs); +} + +template +constexpr auto operator < (const T& lhs, const optional& rhs) -> +decltype(detail::opt::cmp_val(lhs) < detail::opt::cmp_val(rhs)) { - return lhs.base() < rhs.base(); + return detail::opt::cmp_val(lhs) < detail::opt::cmp_val(rhs); } template -constexpr bool operator <= (const optional& lhs, const optional& rhs) +constexpr auto operator <= (const optional& lhs, const optional& rhs) -> +decltype(detail::opt::cmp_val(lhs) <= detail::opt::cmp_val(rhs)) +{ + return detail::opt::cmp_val(lhs) <= detail::opt::cmp_val(rhs); +} + +template +constexpr auto operator <= (const optional& lhs, const U& rhs) -> +decltype(detail::opt::cmp_val(lhs) <= detail::opt::cmp_val(rhs)) +{ + return detail::opt::cmp_val(lhs) <= detail::opt::cmp_val(rhs); +} + +template +constexpr auto operator <= (const T& lhs, const optional& rhs) -> +decltype(detail::opt::cmp_val(lhs) <= detail::opt::cmp_val(rhs)) { - return lhs.base() <= rhs.base(); + return detail::opt::cmp_val(lhs) <= detail::opt::cmp_val(rhs); } template -constexpr bool operator > (const optional& lhs, const optional& rhs) +constexpr auto operator > (const optional& lhs, const optional& rhs) -> +decltype(detail::opt::cmp_val(lhs) > detail::opt::cmp_val(rhs)) +{ + return detail::opt::cmp_val(lhs) > detail::opt::cmp_val(rhs); +} + +template +constexpr auto operator > (const optional& lhs, const U& rhs) -> +decltype(detail::opt::cmp_val(lhs) > detail::opt::cmp_val(rhs)) +{ + return detail::opt::cmp_val(lhs) > detail::opt::cmp_val(rhs); +} + +template +constexpr auto operator > (const T& lhs, const optional& rhs) -> +decltype(detail::opt::cmp_val(lhs) > detail::opt::cmp_val(rhs)) { - return lhs.base() > rhs.base(); + return detail::opt::cmp_val(lhs) > detail::opt::cmp_val(rhs); } template -constexpr bool operator >= (const optional& lhs, const optional& rhs) +constexpr auto operator >= (const optional& lhs, const optional& rhs) -> +decltype(detail::opt::cmp_val(lhs) >= detail::opt::cmp_val(rhs)) +{ + return detail::opt::cmp_val(lhs) >= detail::opt::cmp_val(rhs); +} + +template +constexpr auto operator >= (const optional& lhs, const U& rhs) -> +decltype(detail::opt::cmp_val(lhs) >= detail::opt::cmp_val(rhs)) { - return lhs.base() >= rhs.base(); + return detail::opt::cmp_val(lhs) >= detail::opt::cmp_val(rhs); } -} // namespace nlohmann +template +constexpr auto operator >= (const T& lhs, const optional& rhs) -> +decltype(detail::opt::cmp_val(lhs) >= detail::opt::cmp_val(rhs)) +{ + return detail::opt::cmp_val(lhs) >= detail::opt::cmp_val(rhs); +} + +#endif // JSON_HAS_CPP_20 + +NLOHMANN_JSON_NAMESPACE_END #endif // JSON_HAS_CPP_17 @@ -4794,7 +4947,7 @@ void from_json(const BasicJsonType& j, std::optional& opt) } template -void from_json(const BasicJsonType& j, nlohmann::optional& opt) +void from_json(const BasicJsonType& j, optional& opt) { if (j.is_null()) { @@ -5790,6 +5943,13 @@ void to_json(BasicJsonType& j, const std::optional& opt) noexcept j = nullptr; } } + +template::value, int> = 0> +void to_json(BasicJsonType& j, const optional& opt) noexcept +{ + to_json(j, opt.base()); +} #endif template Date: Sun, 8 Jan 2023 11:55:28 +0500 Subject: [PATCH 04/11] Fix warning about constness --- tests/src/unit-conversions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/src/unit-conversions.cpp b/tests/src/unit-conversions.cpp index 72ef03de21..bb7209b9cf 100644 --- a/tests/src/unit-conversions.cpp +++ b/tests/src/unit-conversions.cpp @@ -1584,7 +1584,7 @@ TEST_CASE("std::optional") SECTION("null") { json j_null; - std::optional opt_null; + std::optional const opt_null; CHECK(json(opt_null) == j_null); CHECK(j_null.get>() == std::nullopt); From 24f2ce74a0167725983c96a19889cedbeb216189 Mon Sep 17 00:00:00 2001 From: Alexander Karzhenkov Date: Sun, 8 Jan 2023 11:55:55 +0500 Subject: [PATCH 05/11] Add noexcept --- include/nlohmann/optional.hpp | 10 +++++----- single_include/nlohmann/json.hpp | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/nlohmann/optional.hpp b/include/nlohmann/optional.hpp index 3dbcd92b6a..f39d2d7ba8 100644 --- a/include/nlohmann/optional.hpp +++ b/include/nlohmann/optional.hpp @@ -71,17 +71,17 @@ class optional public: - base_type& base() & + base_type& base() & noexcept { return base_value; } - const base_type& base() const & + const base_type& base() const & noexcept { return base_value; } - base_type&& base() && + base_type&& base() && noexcept { return std::move(base_value); } @@ -150,8 +150,8 @@ class optional constexpr T* operator ->() noexcept { return base_value.operator ->(); } constexpr const T* operator ->() const noexcept { return base_value.operator ->(); } - operator base_type& () & { return base_value; } - operator base_type&& () && { return std::move(base_value); } + operator base_type& () & noexcept { return base_value; } + operator base_type&& () && noexcept { return std::move(base_value); } // *INDENT-ON* }; diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 85e718af9a..bce245c98f 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -4663,17 +4663,17 @@ class optional public: - base_type& base() & + base_type& base() & noexcept { return base_value; } - const base_type& base() const & + const base_type& base() const & noexcept { return base_value; } - base_type&& base() && + base_type&& base() && noexcept { return std::move(base_value); } @@ -4742,8 +4742,8 @@ class optional constexpr T* operator ->() noexcept { return base_value.operator ->(); } constexpr const T* operator ->() const noexcept { return base_value.operator ->(); } - operator base_type& () & { return base_value; } - operator base_type&& () && { return std::move(base_value); } + operator base_type& () & noexcept { return base_value; } + operator base_type&& () && noexcept { return std::move(base_value); } // *INDENT-ON* }; From 7510f32ff756f36bff3b05785d1e375e5867050d Mon Sep 17 00:00:00 2001 From: Alexander Karzhenkov Date: Sun, 8 Jan 2023 12:00:04 +0500 Subject: [PATCH 06/11] Add comment about a trick for GCC8.1 --- include/nlohmann/optional.hpp | 2 +- single_include/nlohmann/json.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/nlohmann/optional.hpp b/include/nlohmann/optional.hpp index f39d2d7ba8..5638976258 100644 --- a/include/nlohmann/optional.hpp +++ b/include/nlohmann/optional.hpp @@ -65,7 +65,7 @@ class optional is_base_constructible_from >; - struct noexcept_fix_t {}; + struct noexcept_fix_t {}; // trick for GCC8.1 (see default constructor) base_type base_value; diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index bce245c98f..a02d2a6c56 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -4657,7 +4657,7 @@ class optional is_base_constructible_from >; - struct noexcept_fix_t {}; + struct noexcept_fix_t {}; // trick for GCC8.1 (see default constructor) base_type base_value; From 1bef04620ade790853f2712e67ae42be726bf274 Mon Sep 17 00:00:00 2001 From: Alexander Karzhenkov Date: Sun, 8 Jan 2023 12:05:15 +0500 Subject: [PATCH 07/11] Add explicit member initialization to mitigate -Werror=effc++ --- include/nlohmann/optional.hpp | 5 ++++- single_include/nlohmann/json.hpp | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/include/nlohmann/optional.hpp b/include/nlohmann/optional.hpp index 5638976258..dee4d4b5b4 100644 --- a/include/nlohmann/optional.hpp +++ b/include/nlohmann/optional.hpp @@ -86,7 +86,10 @@ class optional return std::move(base_value); } - constexpr optional() noexcept(noexcept(std::optional())) = default; + constexpr optional() noexcept(noexcept(std::optional())) + : base_value() // explicitly initialized to mitigate -Werror=effc++ + { + } constexpr optional(std::nullopt_t /* unused */) noexcept : base_value(std::nullopt) diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index a02d2a6c56..b2d4f1e483 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -4678,7 +4678,10 @@ class optional return std::move(base_value); } - constexpr optional() noexcept(noexcept(std::optional())) = default; + constexpr optional() noexcept(noexcept(std::optional())) + : base_value() // explicitly initialized to mitigate -Werror=effc++ + { + } constexpr optional(std::nullopt_t /* unused */) noexcept : base_value(std::nullopt) From 90264938242c13b25ee121c96d75090804288e88 Mon Sep 17 00:00:00 2001 From: Alexander Karzhenkov Date: Sun, 8 Jan 2023 13:03:47 +0500 Subject: [PATCH 08/11] Fix warning about constness --- tests/src/unit-conversions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/src/unit-conversions.cpp b/tests/src/unit-conversions.cpp index bb7209b9cf..82a4ad9292 100644 --- a/tests/src/unit-conversions.cpp +++ b/tests/src/unit-conversions.cpp @@ -1656,7 +1656,7 @@ TEST_CASE("nlohmann::optional") SECTION("null") { json j_null; - nlohmann::optional opt_null; + nlohmann::optional const opt_null; CHECK(json(opt_null) == j_null); CHECK(j_null.get>() == std::nullopt); From 7745f5987abc9c8c3c0614e6d0c65def9b7de1fc Mon Sep 17 00:00:00 2001 From: Alexander Karzhenkov Date: Sun, 8 Jan 2023 17:34:59 +0500 Subject: [PATCH 09/11] More tests for optional comparisons --- tests/src/unit-optional.cpp | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/src/unit-optional.cpp b/tests/src/unit-optional.cpp index 58829baf60..b561f3cb46 100644 --- a/tests/src/unit-optional.cpp +++ b/tests/src/unit-optional.cpp @@ -35,6 +35,14 @@ TEST_CASE("nlohmann::optional comparison") CHECK_FALSE(nullopt == opt_int(0)); CHECK_FALSE(opt_int(0) == opt_int(1)); + CHECK(opt_int() == std_opt_int()); + CHECK(opt_int(0) == std_opt_int(0)); + CHECK_FALSE(opt_int(0) == std_opt_int(1)); + + CHECK(std_opt_int() == opt_int()); + CHECK(std_opt_int(0) == opt_int(0)); + CHECK_FALSE(std_opt_int(0) == opt_int(1)); + CHECK(opt_int(0) != nullopt); CHECK(nullopt != opt_int(0)); CHECK(opt_int(0) != opt_int(1)); @@ -43,25 +51,45 @@ TEST_CASE("nlohmann::optional comparison") CHECK_FALSE(opt_int() != opt_int(nullopt)); CHECK_FALSE(opt_int(0) != opt_int(0)); + CHECK_FALSE(opt_int() != std_opt_int()); + CHECK_FALSE(opt_int(0) != std_opt_int(0)); + CHECK(opt_int(0) != std_opt_int(1)); + + CHECK_FALSE(std_opt_int() != opt_int()); + CHECK_FALSE(std_opt_int(0) != opt_int(0)); + CHECK(std_opt_int(0) != opt_int(1)); + CHECK(opt_int(0) > nullopt); CHECK(opt_int(1) > opt_int(0)); CHECK_FALSE(nullopt > opt_int(0)); CHECK_FALSE(opt_int(0) > opt_int(1)); + CHECK(opt_int(0) > std_opt_int()); + CHECK(std_opt_int(0) > opt_int()); + CHECK(opt_int(0) >= nullopt); CHECK(opt_int(1) >= opt_int(0)); CHECK_FALSE(nullopt >= opt_int(0)); CHECK_FALSE(opt_int(0) >= opt_int(1)); + CHECK(opt_int(0) >= std_opt_int()); + CHECK(std_opt_int(0) >= opt_int()); + CHECK(nullopt < opt_int(0)); CHECK(opt_int(0) < opt_int(1)); CHECK_FALSE(opt_int(0) < nullopt); CHECK_FALSE(opt_int(1) < opt_int(0)); + CHECK_FALSE(opt_int(0) < std_opt_int()); + CHECK_FALSE(std_opt_int(0) < opt_int()); + CHECK(nullopt <= opt_int(0)); CHECK(opt_int(0) <= opt_int(1)); CHECK_FALSE(opt_int(0) <= nullopt); CHECK_FALSE(opt_int(1) <= opt_int(0)); + + CHECK_FALSE(opt_int(0) <= std_opt_int()); + CHECK_FALSE(std_opt_int(0) <= opt_int()); } TEST_CASE("nlohmann::optional constructors") From d3f72b1b9ca6db536419b2972b65349e666c4c09 Mon Sep 17 00:00:00 2001 From: Alexander Karzhenkov Date: Sun, 8 Jan 2023 17:35:08 +0500 Subject: [PATCH 10/11] Use macros when comparing optional --- include/nlohmann/optional.hpp | 170 ++++++------------------------- single_include/nlohmann/json.hpp | 170 ++++++------------------------- 2 files changed, 66 insertions(+), 274 deletions(-) diff --git a/include/nlohmann/optional.hpp b/include/nlohmann/optional.hpp index dee4d4b5b4..d7cd210cea 100644 --- a/include/nlohmann/optional.hpp +++ b/include/nlohmann/optional.hpp @@ -170,160 +170,56 @@ template const std::optional& cmp_val(const optional& v) { return v.base(); } -template void cmp_val(const std::optional& v) = delete; } // namespace detail::opt -#ifdef JSON_HAS_CPP_20 +#define JSON_OPTIONAL_COMPARISON_EXPR(OP) \ + detail::opt::cmp_val(lhs) OP detail::opt::cmp_val(rhs) -template -auto operator == (const optional& lhs, const U& rhs) -> -decltype(detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs); -} +#define JSON_OPTIONAL_COMPARISON(OP, LHS, RHS) \ + template \ + auto operator OP (const LHS& lhs, const RHS& rhs) \ + noexcept(noexcept(JSON_OPTIONAL_COMPARISON_EXPR(OP))) \ + -> decltype(JSON_OPTIONAL_COMPARISON_EXPR(OP)) \ + { \ + return JSON_OPTIONAL_COMPARISON_EXPR(OP); \ + } + +#ifdef JSON_HAS_CPP_20 // *INDENT-OFF* -template -auto operator <=> (const optional& lhs, const U& rhs) -> -decltype(detail::opt::cmp_val(lhs) <=> detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) <=> detail::opt::cmp_val(rhs); -} +JSON_OPTIONAL_COMPARISON( <=>, optional, B) // *INDENT-ON* -#else // JSON_HAS_CPP_20 - -template -constexpr auto operator == (const optional& lhs, const optional& rhs) -> -decltype(detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs); -} +JSON_OPTIONAL_COMPARISON( ==, optional, B) +JSON_OPTIONAL_COMPARISON( ==, optional, std::optional) +JSON_OPTIONAL_COMPARISON( ==, std::optional, optional) -template -constexpr auto operator == (const optional& lhs, const U& rhs) -> -decltype(detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs); -} - -template -constexpr auto operator == (const T& lhs, const optional& rhs) -> -decltype(detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs); -} - -template -constexpr auto operator != (const optional& lhs, const optional& rhs) -> -decltype(detail::opt::cmp_val(lhs) != detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) != detail::opt::cmp_val(rhs); -} - -template -constexpr auto operator != (const optional& lhs, const U& rhs) -> -decltype(detail::opt::cmp_val(lhs) != detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) != detail::opt::cmp_val(rhs); -} - -template -constexpr auto operator != (const T& lhs, const optional& rhs) -> -decltype(detail::opt::cmp_val(lhs) != detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) != detail::opt::cmp_val(rhs); -} - -template -constexpr auto operator < (const optional& lhs, const optional& rhs) -> -decltype(detail::opt::cmp_val(lhs) < detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) < detail::opt::cmp_val(rhs); -} - -template -constexpr auto operator < (const optional& lhs, const U& rhs) -> -decltype(detail::opt::cmp_val(lhs) < detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) < detail::opt::cmp_val(rhs); -} - -template -constexpr auto operator < (const T& lhs, const optional& rhs) -> -decltype(detail::opt::cmp_val(lhs) < detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) < detail::opt::cmp_val(rhs); -} - -template -constexpr auto operator <= (const optional& lhs, const optional& rhs) -> -decltype(detail::opt::cmp_val(lhs) <= detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) <= detail::opt::cmp_val(rhs); -} - -template -constexpr auto operator <= (const optional& lhs, const U& rhs) -> -decltype(detail::opt::cmp_val(lhs) <= detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) <= detail::opt::cmp_val(rhs); -} - -template -constexpr auto operator <= (const T& lhs, const optional& rhs) -> -decltype(detail::opt::cmp_val(lhs) <= detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) <= detail::opt::cmp_val(rhs); -} - -template -constexpr auto operator > (const optional& lhs, const optional& rhs) -> -decltype(detail::opt::cmp_val(lhs) > detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) > detail::opt::cmp_val(rhs); -} +#else // JSON_HAS_CPP_20 -template -constexpr auto operator > (const optional& lhs, const U& rhs) -> -decltype(detail::opt::cmp_val(lhs) > detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) > detail::opt::cmp_val(rhs); -} +#define JSON_OPTIONAL_COMPARISON_OP(OP) \ + JSON_OPTIONAL_COMPARISON(OP, optional, optional) \ + JSON_OPTIONAL_COMPARISON(OP, optional, std::optional) \ + JSON_OPTIONAL_COMPARISON(OP, std::optional, optional) \ + JSON_OPTIONAL_COMPARISON(OP, optional, B) \ + JSON_OPTIONAL_COMPARISON(OP, A, optional) -template -constexpr auto operator > (const T& lhs, const optional& rhs) -> -decltype(detail::opt::cmp_val(lhs) > detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) > detail::opt::cmp_val(rhs); -} +JSON_OPTIONAL_COMPARISON_OP( == ) +JSON_OPTIONAL_COMPARISON_OP( != ) +JSON_OPTIONAL_COMPARISON_OP( < ) +JSON_OPTIONAL_COMPARISON_OP( <= ) +JSON_OPTIONAL_COMPARISON_OP( > ) +JSON_OPTIONAL_COMPARISON_OP( >= ) -template -constexpr auto operator >= (const optional& lhs, const optional& rhs) -> -decltype(detail::opt::cmp_val(lhs) >= detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) >= detail::opt::cmp_val(rhs); -} - -template -constexpr auto operator >= (const optional& lhs, const U& rhs) -> -decltype(detail::opt::cmp_val(lhs) >= detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) >= detail::opt::cmp_val(rhs); -} - -template -constexpr auto operator >= (const T& lhs, const optional& rhs) -> -decltype(detail::opt::cmp_val(lhs) >= detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) >= detail::opt::cmp_val(rhs); -} +#undef JSON_OPTIONAL_COMPARISON_OP #endif // JSON_HAS_CPP_20 +#undef JSON_OPTIONAL_COMPARISON +#undef JSON_OPTIONAL_COMPARISON_EXPR + NLOHMANN_JSON_NAMESPACE_END #endif // JSON_HAS_CPP_17 diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index b2d4f1e483..67bc932aaa 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -4762,160 +4762,56 @@ template const std::optional& cmp_val(const optional& v) { return v.base(); } -template void cmp_val(const std::optional& v) = delete; } // namespace detail::opt -#ifdef JSON_HAS_CPP_20 +#define JSON_OPTIONAL_COMPARISON_EXPR(OP) \ + detail::opt::cmp_val(lhs) OP detail::opt::cmp_val(rhs) -template -auto operator == (const optional& lhs, const U& rhs) -> -decltype(detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs); -} +#define JSON_OPTIONAL_COMPARISON(OP, LHS, RHS) \ + template \ + auto operator OP (const LHS& lhs, const RHS& rhs) \ + noexcept(noexcept(JSON_OPTIONAL_COMPARISON_EXPR(OP))) \ + -> decltype(JSON_OPTIONAL_COMPARISON_EXPR(OP)) \ + { \ + return JSON_OPTIONAL_COMPARISON_EXPR(OP); \ + } + +#ifdef JSON_HAS_CPP_20 // *INDENT-OFF* -template -auto operator <=> (const optional& lhs, const U& rhs) -> -decltype(detail::opt::cmp_val(lhs) <=> detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) <=> detail::opt::cmp_val(rhs); -} +JSON_OPTIONAL_COMPARISON( <=>, optional, B) // *INDENT-ON* -#else // JSON_HAS_CPP_20 - -template -constexpr auto operator == (const optional& lhs, const optional& rhs) -> -decltype(detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs); -} +JSON_OPTIONAL_COMPARISON( ==, optional, B) +JSON_OPTIONAL_COMPARISON( ==, optional, std::optional) +JSON_OPTIONAL_COMPARISON( ==, std::optional, optional) -template -constexpr auto operator == (const optional& lhs, const U& rhs) -> -decltype(detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs); -} - -template -constexpr auto operator == (const T& lhs, const optional& rhs) -> -decltype(detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) == detail::opt::cmp_val(rhs); -} - -template -constexpr auto operator != (const optional& lhs, const optional& rhs) -> -decltype(detail::opt::cmp_val(lhs) != detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) != detail::opt::cmp_val(rhs); -} - -template -constexpr auto operator != (const optional& lhs, const U& rhs) -> -decltype(detail::opt::cmp_val(lhs) != detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) != detail::opt::cmp_val(rhs); -} - -template -constexpr auto operator != (const T& lhs, const optional& rhs) -> -decltype(detail::opt::cmp_val(lhs) != detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) != detail::opt::cmp_val(rhs); -} - -template -constexpr auto operator < (const optional& lhs, const optional& rhs) -> -decltype(detail::opt::cmp_val(lhs) < detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) < detail::opt::cmp_val(rhs); -} - -template -constexpr auto operator < (const optional& lhs, const U& rhs) -> -decltype(detail::opt::cmp_val(lhs) < detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) < detail::opt::cmp_val(rhs); -} - -template -constexpr auto operator < (const T& lhs, const optional& rhs) -> -decltype(detail::opt::cmp_val(lhs) < detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) < detail::opt::cmp_val(rhs); -} - -template -constexpr auto operator <= (const optional& lhs, const optional& rhs) -> -decltype(detail::opt::cmp_val(lhs) <= detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) <= detail::opt::cmp_val(rhs); -} - -template -constexpr auto operator <= (const optional& lhs, const U& rhs) -> -decltype(detail::opt::cmp_val(lhs) <= detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) <= detail::opt::cmp_val(rhs); -} - -template -constexpr auto operator <= (const T& lhs, const optional& rhs) -> -decltype(detail::opt::cmp_val(lhs) <= detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) <= detail::opt::cmp_val(rhs); -} - -template -constexpr auto operator > (const optional& lhs, const optional& rhs) -> -decltype(detail::opt::cmp_val(lhs) > detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) > detail::opt::cmp_val(rhs); -} +#else // JSON_HAS_CPP_20 -template -constexpr auto operator > (const optional& lhs, const U& rhs) -> -decltype(detail::opt::cmp_val(lhs) > detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) > detail::opt::cmp_val(rhs); -} +#define JSON_OPTIONAL_COMPARISON_OP(OP) \ + JSON_OPTIONAL_COMPARISON(OP, optional, optional) \ + JSON_OPTIONAL_COMPARISON(OP, optional, std::optional) \ + JSON_OPTIONAL_COMPARISON(OP, std::optional, optional) \ + JSON_OPTIONAL_COMPARISON(OP, optional, B) \ + JSON_OPTIONAL_COMPARISON(OP, A, optional) -template -constexpr auto operator > (const T& lhs, const optional& rhs) -> -decltype(detail::opt::cmp_val(lhs) > detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) > detail::opt::cmp_val(rhs); -} +JSON_OPTIONAL_COMPARISON_OP( == ) +JSON_OPTIONAL_COMPARISON_OP( != ) +JSON_OPTIONAL_COMPARISON_OP( < ) +JSON_OPTIONAL_COMPARISON_OP( <= ) +JSON_OPTIONAL_COMPARISON_OP( > ) +JSON_OPTIONAL_COMPARISON_OP( >= ) -template -constexpr auto operator >= (const optional& lhs, const optional& rhs) -> -decltype(detail::opt::cmp_val(lhs) >= detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) >= detail::opt::cmp_val(rhs); -} - -template -constexpr auto operator >= (const optional& lhs, const U& rhs) -> -decltype(detail::opt::cmp_val(lhs) >= detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) >= detail::opt::cmp_val(rhs); -} - -template -constexpr auto operator >= (const T& lhs, const optional& rhs) -> -decltype(detail::opt::cmp_val(lhs) >= detail::opt::cmp_val(rhs)) -{ - return detail::opt::cmp_val(lhs) >= detail::opt::cmp_val(rhs); -} +#undef JSON_OPTIONAL_COMPARISON_OP #endif // JSON_HAS_CPP_20 +#undef JSON_OPTIONAL_COMPARISON +#undef JSON_OPTIONAL_COMPARISON_EXPR + NLOHMANN_JSON_NAMESPACE_END #endif // JSON_HAS_CPP_17 From 14e33189295399e9ead4d08309b1d1b21f4040d0 Mon Sep 17 00:00:00 2001 From: Alexander Karzhenkov Date: Sun, 8 Jan 2023 18:32:30 +0500 Subject: [PATCH 11/11] Remove noexcept from optional comparisons --- include/nlohmann/optional.hpp | 1 - single_include/nlohmann/json.hpp | 1 - 2 files changed, 2 deletions(-) diff --git a/include/nlohmann/optional.hpp b/include/nlohmann/optional.hpp index d7cd210cea..9b5f09bde6 100644 --- a/include/nlohmann/optional.hpp +++ b/include/nlohmann/optional.hpp @@ -179,7 +179,6 @@ template const std::optional& cmp_val(const optional& v) #define JSON_OPTIONAL_COMPARISON(OP, LHS, RHS) \ template \ auto operator OP (const LHS& lhs, const RHS& rhs) \ - noexcept(noexcept(JSON_OPTIONAL_COMPARISON_EXPR(OP))) \ -> decltype(JSON_OPTIONAL_COMPARISON_EXPR(OP)) \ { \ return JSON_OPTIONAL_COMPARISON_EXPR(OP); \ diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 67bc932aaa..8045fb147c 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -4771,7 +4771,6 @@ template const std::optional& cmp_val(const optional& v) #define JSON_OPTIONAL_COMPARISON(OP, LHS, RHS) \ template \ auto operator OP (const LHS& lhs, const RHS& rhs) \ - noexcept(noexcept(JSON_OPTIONAL_COMPARISON_EXPR(OP))) \ -> decltype(JSON_OPTIONAL_COMPARISON_EXPR(OP)) \ { \ return JSON_OPTIONAL_COMPARISON_EXPR(OP); \