diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index aa2f0cbf4..83c1a341e 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -28,6 +28,7 @@ #include #include #include +#include NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail @@ -43,6 +44,34 @@ 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(); + } +} + +template +void from_json(const BasicJsonType& j, 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 562089c33..fbc8e694f 100644 --- a/include/nlohmann/detail/conversions/to_json.hpp +++ b/include/nlohmann/detail/conversions/to_json.hpp @@ -23,6 +23,7 @@ #include #include #include +#include NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail @@ -260,6 +261,29 @@ struct external_constructor // to_json // ///////////// +#ifdef JSON_HAS_CPP_17 +template::value, int> = 0> +void to_json(BasicJsonType& j, const std::optional& opt) noexcept +{ + if (opt.has_value()) + { + j = *opt; + } + else + { + j = nullptr; + } +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, const optional& opt) noexcept +{ + to_json(j, opt.base()); +} +#endif + template::value, int> = 0> inline void to_json(BasicJsonType& j, T b) noexcept diff --git a/include/nlohmann/optional.hpp b/include/nlohmann/optional.hpp new file mode 100644 index 000000000..9b5f09bde --- /dev/null +++ b/include/nlohmann/optional.hpp @@ -0,0 +1,224 @@ +#pragma once + +#include + +#ifdef JSON_HAS_CPP_17 + +#include +#include + +NLOHMANN_JSON_NAMESPACE_BEGIN + +template +class optional +{ + // *INDENT-OFF* + + using base_type = std::optional; + using value_type = T; + + 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 {}; // trick for GCC8.1 (see default constructor) + + base_type base_value; + + public: + + base_type& base() & noexcept + { + return base_value; + } + + const base_type& base() const & noexcept + { + return base_value; + } + + base_type&& base() && noexcept + { + return std::move(base_value); + } + + 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) + { + } + + template = 0> + constexpr optional(U&& value) + noexcept(noexcept( + base_type(std::forward(value).operator optional()) + )) : + base_value(std::forward(value).operator optional()) + { + } + + template = 0> + constexpr optional(U&& value) + noexcept(noexcept( + base_type(std::forward(value)) + )) : + base_value(std::forward(value)) + { + } + + template = 0> + explicit + constexpr optional(U&& value) + noexcept(noexcept( + base_type(std::forward(value)) + )) : + base_value(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_value(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_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& () & noexcept { return base_value; } + operator base_type&& () && noexcept { 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(); +} + +} // namespace detail::opt + +#define JSON_OPTIONAL_COMPARISON_EXPR(OP) \ + detail::opt::cmp_val(lhs) OP detail::opt::cmp_val(rhs) + +#define JSON_OPTIONAL_COMPARISON(OP, LHS, RHS) \ + template \ + auto operator OP (const LHS& lhs, const RHS& rhs) \ + -> decltype(JSON_OPTIONAL_COMPARISON_EXPR(OP)) \ + { \ + return JSON_OPTIONAL_COMPARISON_EXPR(OP); \ + } + +#ifdef JSON_HAS_CPP_20 + +// *INDENT-OFF* + +JSON_OPTIONAL_COMPARISON( <=>, optional, B) + +// *INDENT-ON* + +JSON_OPTIONAL_COMPARISON( ==, optional, B) +JSON_OPTIONAL_COMPARISON( ==, optional, std::optional) +JSON_OPTIONAL_COMPARISON( ==, std::optional, optional) + +#else // JSON_HAS_CPP_20 + +#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) + +JSON_OPTIONAL_COMPARISON_OP( == ) +JSON_OPTIONAL_COMPARISON_OP( != ) +JSON_OPTIONAL_COMPARISON_OP( < ) +JSON_OPTIONAL_COMPARISON_OP( <= ) +JSON_OPTIONAL_COMPARISON_OP( > ) +JSON_OPTIONAL_COMPARISON_OP( >= ) + +#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 a858728c4..fb7b062d3 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -4655,6 +4655,233 @@ NLOHMANN_JSON_NAMESPACE_END // #include +// #include + + +// #include + + +#ifdef JSON_HAS_CPP_17 + +#include +#include + +NLOHMANN_JSON_NAMESPACE_BEGIN + +template +class optional +{ + // *INDENT-OFF* + + using base_type = std::optional; + using value_type = T; + + 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 {}; // trick for GCC8.1 (see default constructor) + + base_type base_value; + + public: + + base_type& base() & noexcept + { + return base_value; + } + + const base_type& base() const & noexcept + { + return base_value; + } + + base_type&& base() && noexcept + { + return std::move(base_value); + } + + 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) + { + } + + template = 0> + constexpr optional(U&& value) + noexcept(noexcept( + base_type(std::forward(value).operator optional()) + )) : + base_value(std::forward(value).operator optional()) + { + } + + template = 0> + constexpr optional(U&& value) + noexcept(noexcept( + base_type(std::forward(value)) + )) : + base_value(std::forward(value)) + { + } + + template = 0> + explicit + constexpr optional(U&& value) + noexcept(noexcept( + base_type(std::forward(value)) + )) : + base_value(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_value(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_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& () & noexcept { return base_value; } + operator base_type&& () && noexcept { 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(); +} + +} // namespace detail::opt + +#define JSON_OPTIONAL_COMPARISON_EXPR(OP) \ + detail::opt::cmp_val(lhs) OP detail::opt::cmp_val(rhs) + +#define JSON_OPTIONAL_COMPARISON(OP, LHS, RHS) \ + template \ + auto operator OP (const LHS& lhs, const RHS& rhs) \ + -> decltype(JSON_OPTIONAL_COMPARISON_EXPR(OP)) \ + { \ + return JSON_OPTIONAL_COMPARISON_EXPR(OP); \ + } + +#ifdef JSON_HAS_CPP_20 + +// *INDENT-OFF* + +JSON_OPTIONAL_COMPARISON( <=>, optional, B) + +// *INDENT-ON* + +JSON_OPTIONAL_COMPARISON( ==, optional, B) +JSON_OPTIONAL_COMPARISON( ==, optional, std::optional) +JSON_OPTIONAL_COMPARISON( ==, std::optional, optional) + +#else // JSON_HAS_CPP_20 + +#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) + +JSON_OPTIONAL_COMPARISON_OP( == ) +JSON_OPTIONAL_COMPARISON_OP( != ) +JSON_OPTIONAL_COMPARISON_OP( < ) +JSON_OPTIONAL_COMPARISON_OP( <= ) +JSON_OPTIONAL_COMPARISON_OP( > ) +JSON_OPTIONAL_COMPARISON_OP( >= ) + +#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 + NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail @@ -4670,6 +4897,34 @@ 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(); + } +} + +template +void from_json(const BasicJsonType& j, 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&& @@ -5400,6 +5655,8 @@ class tuple_element> // #include +// #include + NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail @@ -5637,6 +5894,29 @@ struct external_constructor // to_json // ///////////// +#ifdef JSON_HAS_CPP_17 +template::value, int> = 0> +void to_json(BasicJsonType& j, const std::optional& opt) noexcept +{ + if (opt.has_value()) + { + j = *opt; + } + else + { + j = nullptr; + } +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, const optional& opt) noexcept +{ + to_json(j, opt.base()); +} +#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 d7df0bee5..520c9dc87 100644 --- a/tests/src/unit-conversions.cpp +++ b/tests/src/unit-conversions.cpp @@ -31,6 +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") + +// 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") { @@ -1569,4 +1578,147 @@ TEST_CASE("JSON to enum mapping") } } +#ifdef JSON_HAS_CPP_17 +TEST_CASE("std::optional") +{ + SECTION("null") + { + json j_null; + std::optional const opt_null; + + CHECK(json(opt_null) == j_null); + 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") + { + 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(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); + } +} + +TEST_CASE("nlohmann::optional") +{ + SECTION("null") + { + json j_null; + nlohmann::optional const 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 + DOCTEST_CLANG_SUPPRESS_WARNING_POP diff --git a/tests/src/unit-optional.cpp b/tests/src/unit-optional.cpp new file mode 100644 index 000000000..b561f3cb4 --- /dev/null +++ b/tests/src/unit-optional.cpp @@ -0,0 +1,181 @@ +#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() == 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)); + 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_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") +{ + 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).base()) == 222); + } + + SECTION("2") + { + opt2 = std::as_const(opt1).base(); + 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