From a297e307ce83b6153445da4b671e659ec004e015 Mon Sep 17 00:00:00 2001 From: Ken Matsui <26405363+ken-matsui@users.noreply.github.com> Date: Tue, 23 Jul 2024 08:44:31 -0700 Subject: [PATCH] Support std::optional members for TOML11_DEFINE_CONVERSION_NON_INTRUSIVE --- include/toml11/conversion.hpp | 75 +++++++++++++++++++++- include/toml11/version.hpp | 6 ++ tests/test_user_defined_conversion.cpp | 86 +++++++++++++++++++++++++- 3 files changed, 164 insertions(+), 3 deletions(-) diff --git a/include/toml11/conversion.hpp b/include/toml11/conversion.hpp index 89784687..aab80110 100644 --- a/include/toml11/conversion.hpp +++ b/include/toml11/conversion.hpp @@ -1,9 +1,80 @@ #ifndef TOML11_CONVERSION_HPP #define TOML11_CONVERSION_HPP +#include "find.hpp" #include "from.hpp" // IWYU pragma: keep #include "into.hpp" // IWYU pragma: keep +#if defined(TOML11_HAS_OPTIONAL) + +#include + +namespace toml +{ +namespace detail +{ + +template +inline constexpr bool is_optional_v = false; + +template +inline constexpr bool is_optional_v> = true; + +template +void find_member_variable_from_value(T& obj, const toml::basic_value& v, const char* var_name) +{ + if constexpr(is_optional_v) + { + try + { + obj = toml::find(v, var_name); + } + catch(...) + { + obj = std::nullopt; + } + } + else + { + obj = toml::find(v, var_name); + } +} + +template +void assign_member_variable_to_value(const T& obj, toml::basic_value& v, const char* var_name) +{ + if constexpr(is_optional_v) + { + if(obj.has_value()) + { + v[var_name] = obj.value(); + } + } + else + { + v[var_name] = obj; + } +} + +} // detail +} // toml + +#else + +template +void find_member_variable_from_value(T& obj, const toml::basic_value& v, const char* var_name) +{ + obj = toml::find(v, var_name); +} + +template +void assign_member_variable_to_value(const T& obj, toml::basic_value& v, const char* var_name) +{ + v[var_name] = obj; +} + +#endif // optional + // use it in the following way. // ```cpp // namespace foo @@ -88,10 +159,10 @@ #define TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE(VAR_NAME)\ - obj.VAR_NAME = toml::find(v, TOML11_STRINGIZE(VAR_NAME)); + toml::detail::find_member_variable_from_value(obj.VAR_NAME, v, TOML11_STRINGIZE(VAR_NAME)); #define TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE(VAR_NAME)\ - v[TOML11_STRINGIZE(VAR_NAME)] = obj.VAR_NAME; + toml::detail::assign_member_variable_to_value(obj.VAR_NAME, v, TOML11_STRINGIZE(VAR_NAME)); #define TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(NAME, ...)\ namespace toml { \ diff --git a/include/toml11/version.hpp b/include/toml11/version.hpp index 98dc44d7..9f8990b7 100644 --- a/include/toml11/version.hpp +++ b/include/toml11/version.hpp @@ -77,6 +77,12 @@ # endif #endif +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE +# if __has_include() +# define TOML11_HAS_OPTIONAL 1 +# endif +#endif + #if defined(TOML11_COMPILE_SOURCES) # define TOML11_INLINE #else diff --git a/tests/test_user_defined_conversion.cpp b/tests/test_user_defined_conversion.cpp index e7f85206..2266f6db 100644 --- a/tests/test_user_defined_conversion.cpp +++ b/tests/test_user_defined_conversion.cpp @@ -4,6 +4,10 @@ #include #include "utility.hpp" +#if defined(TOML11_HAS_OPTIONAL) +#include +#endif + namespace extlib { struct foo @@ -234,7 +238,7 @@ TEST_CASE("test_conversion_by_member_methods") CHECK(v == v2); } - + { const toml::value v(toml::table{{"a", 42}, {"b", "baz"}}); @@ -652,4 +656,84 @@ TEST_CASE("test_conversion_via_macro") CHECK(v2 == v); } } + +#if defined(TOML11_HAS_OPTIONAL) +namespace extlib4 +{ +struct foo +{ + std::optional a; + std::optional b; +}; +struct bar +{ + std::optional a; + std::optional b; + std::optional f; +}; + +} // extlib4 + +TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(extlib4::foo, a, b) +TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(extlib4::bar, a, b, f) + +TEST_CASE("test_optional_conversion_via_macro") +{ + { + const toml::value v(toml::table{{"a", 42}}); + + const auto foo = toml::get(v); + CHECK(foo.a.value() == 42); + CHECK(foo.b == std::nullopt); + + const toml::value v2(foo); + CHECK(v2 == v); + } + { + const toml::ordered_value v(toml::ordered_table{ + {"b", "baz"} + }); + + const auto foo = toml::get(v); + CHECK(foo.a == std::nullopt); + CHECK(foo.b.value() == "baz"); + + const toml::ordered_value v2(foo); + CHECK(v2 == v); + } + + // ----------------------------------------------------------------------- + + { + const toml::value v(toml::table{ + {"b", "bar.b"}, + {"f", toml::table{{"a", 42}}} + }); + + const auto bar = toml::get(v); + CHECK(bar.a == std::nullopt); + CHECK(bar.b.value() == "bar.b"); + CHECK(bar.f.value().a.value() == 42); + CHECK(bar.f.value().b == std::nullopt); + + const toml::value v2(bar); + CHECK(v2 == v); + } + { + const toml::ordered_value v(toml::ordered_table{ + {"a", 42}, + {"f", toml::ordered_table{{"b", "foo.b"}}} + }); + + const auto bar = toml::get(v); + CHECK(bar.a.value() == 42); + CHECK(bar.b == std::nullopt); + CHECK(bar.f.value().a == std::nullopt); + CHECK(bar.f.value().b.value() == "foo.b"); + + const toml::ordered_value v2(bar); + CHECK(v2 == v); + } +} +#endif // TOML11_HAS_OPTIONAL #endif // TOML11_WITHOUT_DEFINE_NON_INTRUSIVE