]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
LuaWrapper: Add support for `std::variant`
authorRemi Gacogne <remi.gacogne@powerdns.com>
Mon, 8 Dec 2025 14:05:39 +0000 (15:05 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Mon, 8 Dec 2025 14:05:39 +0000 (15:05 +0100)
Signed-off-by: Remi Gacogne <remi.gacogne@powerdns.com>
ext/luawrapper/include/LuaContext.hpp
pdns/test-luawrapper.cc

index 7bc2a747959c8ef8710b52843762b526cf2176bd..7a333dd44469fab4a1ac6e27f12b3d517d2261c2 100644 (file)
@@ -47,6 +47,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <tuple>
 #include <type_traits>
 #include <unordered_map>
+#include <variant>
 #include <boost/any.hpp>
 #include <boost/format.hpp>
 #include <boost/mpl/distance.hpp>
@@ -2556,6 +2557,23 @@ private:
     };
 };
 
+// std::variant
+template<typename... TTypes>
+struct LuaContext::Pusher<std::variant<TTypes...>>
+{
+    static const int minSize = PusherMinSize<TTypes...>::size;
+    static const int maxSize = PusherMaxSize<TTypes...>::size;
+
+    static PushedObject push(lua_State* state, const std::variant<TTypes...>& value) noexcept {
+        PushedObject obj{state, 0};
+        std::visit([&](auto&& arg) {
+            using T = std::decay_t<decltype(arg)>;
+            obj = Pusher<T>::push(state, arg);
+        }, value);
+        return obj;
+    }
+};
+
 // boost::optional
 template<typename TType>
 struct LuaContext::Pusher<boost::optional<TType>> {
@@ -2961,7 +2979,7 @@ struct LuaContext::Reader<std::optional<TType>>
 };
 // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
 
-// variant
+// boost::variant
 template<typename... TTypes>
 struct LuaContext::Reader<boost::variant<TTypes...>>
 {
@@ -3008,6 +3026,36 @@ public:
     }
 };
 
+// std::variant
+template<typename... TTypes>
+struct LuaContext::Reader<std::variant<TTypes...>>
+{
+    using ReturnType = std::variant<TTypes...>;
+
+private:
+    template<std::size_t I = 0> static boost::optional<ReturnType> variantRead(lua_State* state, int index)
+    {
+        constexpr auto nbTypes = std::variant_size_v<ReturnType>;
+        if constexpr (I >= nbTypes) {
+            return boost::none;
+        }
+        if (const auto val = Reader<std::variant_alternative_t<I, ReturnType>>::read(state, index)) {
+            return ReturnType{*val};
+        }
+        if constexpr (I < (nbTypes - 1)) {
+            return variantRead<I + 1>(state, index);
+        }
+        return boost::none;
+    }
+
+public:
+    static auto read(lua_State* state, int index)
+        -> boost::optional<ReturnType>
+    {
+        return variantRead(state, index);
+    }
+};
+
 // 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
index cd61d89ca402e660228f35a5b095043a00ae5662..9aae68dca8c8932814ba5bbbce9b0ce273f15a28 100644 (file)
@@ -70,4 +70,31 @@ BOOST_AUTO_TEST_CASE(test_std_optional)
   }
 }
 
+BOOST_AUTO_TEST_CASE(test_std_variant)
+{
+  LuaContext context;
+  context.writeFunction("testVariant", [](std::variant<int, std::string> incoming) -> std::variant<int, std::string> {
+    return incoming;
+  });
+
+  {
+    auto result = context.executeCode<std::variant<int, std::string>>("return testVariant(1)");
+    BOOST_REQUIRE(std::holds_alternative<int>(result));
+    BOOST_CHECK_EQUAL(std::get<int>(result), 1);
+  }
+
+  {
+    auto result = context.executeCode<std::variant<int, std::string>>("return testVariant('foo')");
+    BOOST_REQUIRE(std::holds_alternative<std::string>(result));
+    BOOST_CHECK_EQUAL(std::get<std::string>(result), "foo");
+  }
+
+  {
+    auto func = [&]() {
+      context.executeCode<std::variant<int, std::string>>("return testVariant(nil)");
+    };
+    BOOST_CHECK_THROW(func(), LuaContext::ExecutionErrorException);
+  }
+}
+
 BOOST_AUTO_TEST_SUITE_END()