diff --git a/ext/luawrapper/include/LuaContext.hpp b/ext/luawrapper/include/LuaContext.hpp index 7bc2a747959c..90246dfef147 100644 --- a/ext/luawrapper/include/LuaContext.hpp +++ b/ext/luawrapper/include/LuaContext.hpp @@ -38,7 +38,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include -#include #include #include #include @@ -56,6 +55,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#if __cplusplus >= 201703L +#include +#include +#endif /* C++17 */ + // Set the older gcc define for tsan if clang or modern gcc tell us we have tsan. #if defined(__has_feature) #if __has_feature(thread_sanitizer) @@ -2001,8 +2005,10 @@ struct LuaContext::FunctionArgumentsCounter<> { // implementation of IsOptional template struct LuaContext::IsOptional> : public std::true_type {}; +#if __cplusplus >= 201703L template struct LuaContext::IsOptional> : public std::true_type {}; +#endif /* C++ 17 */ // implementation of LuaFunctionCaller template @@ -2556,6 +2562,25 @@ struct LuaContext::Pusher> }; }; +#if __cplusplus >= 201703L +// std::variant +template +struct LuaContext::Pusher> +{ + static const int minSize = PusherMinSize::size; + static const int maxSize = PusherMaxSize::size; + + static PushedObject push(lua_State* state, const std::variant& value) noexcept { + PushedObject obj{state, 0}; + std::visit([&](auto&& arg) { + using T = std::decay_t; + obj = Pusher::push(state, arg); + }, value); + return obj; + } +}; +#endif /* C++17 */ + // boost::optional template struct LuaContext::Pusher> { @@ -2575,6 +2600,7 @@ struct LuaContext::Pusher> { } }; +#if __cplusplus >= 201703L // std::optional template struct LuaContext::Pusher> { @@ -2593,6 +2619,7 @@ struct LuaContext::Pusher> { } } }; +#endif /* C++17 */ // tuple template @@ -2943,6 +2970,7 @@ struct LuaContext::Reader> } }; +#if __cplusplus >= 201703L // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) template struct LuaContext::Reader> @@ -2960,8 +2988,9 @@ struct LuaContext::Reader> } }; // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) +#endif /* C++ 17 */ -// variant +// boost::variant template struct LuaContext::Reader> { @@ -3008,6 +3037,42 @@ struct LuaContext::Reader> } }; +#if __cplusplus >= 201703L +// std::variant +template +struct LuaContext::Reader> +{ + using ReturnType = std::variant; + +private: + template static boost::optional variantRead(lua_State* state, int index) + { + constexpr auto nbTypes = std::variant_size_v; + if constexpr (I >= nbTypes) { + return boost::none; + } + using InitialType = std::variant_alternative_t; + using DecayedType = std::decay_t; + if (const auto val = Reader::read(state, index)) { + // we are using the initial, non-decayed type so that a + // cv-qualifiers are not ignored + return ReturnType{std::in_place_type, *val}; + } + if constexpr (I < (nbTypes - 1)) { + return variantRead(state, index); + } + return boost::none; + } + +public: + static auto read(lua_State* state, int index) + -> boost::optional + { + return variantRead(state, index); + } +}; +#endif /* C++ 17 */ + // reading a tuple // tuple have an additional argument for their functions, that is the maximum size to read // if maxSize is smaller than the tuple size, then the remaining parameters will be left to default value diff --git a/pdns/test-luawrapper.cc b/pdns/test-luawrapper.cc index cd61d89ca402..d631b85265a6 100644 --- a/pdns/test-luawrapper.cc +++ b/pdns/test-luawrapper.cc @@ -70,4 +70,64 @@ BOOST_AUTO_TEST_CASE(test_std_optional) } } +BOOST_AUTO_TEST_CASE(test_boost_variant) +{ + using MyVariantType = boost::variant; + + LuaContext context; + context.writeFunction("testVariant", [](MyVariantType incoming) -> MyVariantType { + return incoming; + }); + + { + auto result = context.executeCode("return testVariant(1)"); + const auto* content = boost::get(&result); + BOOST_REQUIRE(content); + BOOST_CHECK_EQUAL(*content, 1); + } + + { + auto result = context.executeCode("return testVariant('foo')"); + const auto* content = boost::get(&result); + BOOST_REQUIRE(content); + BOOST_CHECK_EQUAL(*content, "foo"); + } + + { + auto func = [&]() { + context.executeCode("return testVariant(nil)"); + }; + BOOST_CHECK_THROW(func(), LuaContext::ExecutionErrorException); + } +} + +BOOST_AUTO_TEST_CASE(test_std_variant) +{ + using MyVariantType = std::variant; + + LuaContext context; + context.writeFunction("testVariant", [](MyVariantType incoming) -> MyVariantType { + return incoming; + }); + + { + const auto result = context.executeCode("return testVariant(1)"); + BOOST_REQUIRE(std::holds_alternative(result)); + BOOST_CHECK_EQUAL(std::get(result), 1); + } + + { + const auto result = context.executeCode("return testVariant('foo')"); + BOOST_REQUIRE(std::holds_alternative(result)); + BOOST_CHECK_EQUAL(std::get(result), "foo"); + } + + { + auto func = [&]() { + context.executeCode("return testVariant(nil)"); + }; + BOOST_CHECK_THROW(func(), LuaContext::ExecutionErrorException); + } +} + BOOST_AUTO_TEST_SUITE_END()