From b5ad49485a31c397aad0afc5c655ed335225648a Mon Sep 17 00:00:00 2001 From: Jarod42 Date: Mon, 4 Mar 2024 13:58:03 +0100 Subject: [PATCH] Fix `LuaCallback`. --- CMakeLists.txt | 2 + src/include/luacallback.h | 34 ++++++- src/stratagus/luacallback.cpp | 3 +- tests/stratagus/test_luacallback.cpp | 136 +++++++++++++++++++++++++++ 4 files changed, 173 insertions(+), 2 deletions(-) create mode 100644 tests/stratagus/test_luacallback.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 69c3e8ef95..b2ebb2240b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -531,6 +531,8 @@ set(stratagus_generic_HDRS set(stratagus_tests_SRCS tests/main.cpp + tests/stratagus/test_depend.cpp + tests/stratagus/test_luacallback.cpp tests/stratagus/test_util.cpp tests/network/test_net_lowlevel.cpp tests/network/test_netconnect.cpp diff --git a/src/include/luacallback.h b/src/include/luacallback.h index 042978bbe0..3ad53ac433 100644 --- a/src/include/luacallback.h +++ b/src/include/luacallback.h @@ -41,11 +41,31 @@ using lua_Object = int; // from tolua++.h struct lua_State; +namespace details +{ +template +auto reverse_tuple(TUPLE &&tuple, std::index_sequence) +{ + return std::tuple{std::get(std::forward(tuple))...}; +} + +template +auto reverse_tuple(TUPLE &&tuple) +{ + return reverse_tuple(std::forward(tuple), + std::make_index_sequence>>()); +} +} + + class LuaCallback { public: LuaCallback(lua_State *lua, lua_Object luaref); + LuaCallback(const LuaCallback &) = default; + LuaCallback &operator=(const LuaCallback &) = default; ~LuaCallback(); + void pushPreamble(); void pushInteger(int value); void pushIntegers(const std::vector &values); @@ -90,11 +110,23 @@ class LuaCallback if constexpr (sizeof...(Res) <= 1) { return (popT(), ...); } else { - return std::tuple{popT()...}; + return popTs(std::make_index_sequence()); } } + explicit operator bool() const { return luastate != nullptr; } + +private: + template + auto popTs(std::index_sequence) + { + using RevTuple = + std::tuple>...>; + return details::reverse_tuple(std::tuple{popT>()...}); + } + private: + std::shared_ptr refcounter; lua_State *luastate; int luaref; int arguments; diff --git a/src/stratagus/luacallback.cpp b/src/stratagus/luacallback.cpp index 51b42dcee0..dcb265a395 100644 --- a/src/stratagus/luacallback.cpp +++ b/src/stratagus/luacallback.cpp @@ -50,6 +50,8 @@ LuaCallback::LuaCallback(lua_State *l, lua_Object f) : } lua_pushvalue(l, f); luaref = luaL_ref(l, LUA_REGISTRYINDEX); + refcounter = + std::shared_ptr(nullptr, [this](void*) { luaL_unref(luastate, LUA_REGISTRYINDEX, luaref); }); } /** @@ -189,7 +191,6 @@ LuaCallback::~LuaCallback() if (rescount) { ErrorPrint("There are still some results that weren't popped from stack\n"); } - luaL_unref(luastate, LUA_REGISTRYINDEX, luaref); } //@} diff --git a/tests/stratagus/test_luacallback.cpp b/tests/stratagus/test_luacallback.cpp new file mode 100644 index 0000000000..c9e7e93259 --- /dev/null +++ b/tests/stratagus/test_luacallback.cpp @@ -0,0 +1,136 @@ +// _________ __ __ +// / _____// |_____________ _/ |______ ____ __ __ ______ +// \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/ +// / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ | +// /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ > +// \/ \/ \//_____/ \/ +// ______________________ ______________________ +// T H E W A R B E G I N S +// Stratagus - A free fantasy real time strategy game engine +// +/**@name test_util.cpp - The test file for util.cpp. */ +// +// (c) Copyright 2024 by Joris Dauphin +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; only version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// + +#include + +#include "stratagus.h" + +#include "luacallback.h" +#include "script.h" + +namespace doctest +{ +template +struct StringMaker> +{ + static String convert(const std::tuple &value) + { + String res = "{"; + String sep = ""; + std::apply([&](const auto &...args) { ((res += sep + toString(args), sep = ", "), ...); }, + value); + res += "}"; + return res; + } +}; +} // namespace doctest + +namespace +{ +[[nodiscard]] auto InitLuaCallback(std::string content) +{ + InitLua(); + CHECK(Lua); + + const int status = luaL_loadbuffer(Lua, content.data(), content.size(), "test"); + CHECK(status == 0); + LuaCall(Lua, 0, 0, lua_gettop(Lua), false); + + struct S + { + S() {} + S(const S &) = delete; + ~S() + { + if (Lua) { + lua_close(Lua); + Lua = nullptr; + } + } + }; + return S(); // copy-elision +} +} // namespace + +TEST_CASE("LuaCallback_simplecall") +{ + const auto raii = InitLuaCallback(R"(function f(x) return x end)"); + + lua_getglobal(Lua, "f"); + LuaCallback f(Lua, -1); + + REQUIRE(f.call(42) == 42); +} + +TEST_CASE("LuaCallback_complexcall") +{ + const auto raii = InitLuaCallback(R"(function f(x, s, v) return #s, x == 42, v[3], v[4] end)"); + + lua_getglobal(Lua, "f"); + LuaCallback f(Lua, -1); + + auto t = f.call(42, "toto", std::vector{11, 12, 13, 14}); + REQUIRE(t == std::tuple{4, true, 13, 14}); +} + +TEST_CASE("LuaCallback_copy") +{ + const auto raii = InitLuaCallback(R"(function f(x) return x end)"); + + lua_getglobal(Lua, "f"); + LuaCallback f(Lua, -1); + + { + LuaCallback g(f); + REQUIRE(f.call(42) == 42); + REQUIRE(g.call(42) == 42); + } // g destructor + + REQUIRE(f.call(42) == 42); +} + +TEST_CASE("LuaCallback_assign") +{ + const auto raii = InitLuaCallback(R"(function f(x) return x end function g(x) return x + 1 end)"); + + lua_getglobal(Lua, "f"); + LuaCallback f(Lua, -1); + + { + lua_getglobal(Lua, "g"); + LuaCallback g(Lua, -1); + REQUIRE(f.call(42) == 42); + REQUIRE(g.call(41) == 42); + f = g; + REQUIRE(f.call(41) == 42); + REQUIRE(g.call(41) == 42); + } // g destructor + + REQUIRE(f.call(41) == 42); +}