Skip to content

Commit

Permalink
Fix LuaCallback.
Browse files Browse the repository at this point in the history
  • Loading branch information
Jarod42 committed Mar 4, 2024
1 parent 336761d commit e9e9337
Show file tree
Hide file tree
Showing 4 changed files with 173 additions and 2 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
34 changes: 33 additions & 1 deletion src/include/luacallback.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,31 @@
using lua_Object = int; // from tolua++.h
struct lua_State;

namespace details
{
template <typename TUPLE, std::size_t... Is>
auto reverse_tuple(TUPLE &&tuple, std::index_sequence<Is...>)
{
return std::tuple{std::get<sizeof...(Is) - Is - 1>(std::forward<TUPLE>(tuple))...};
}

template <typename TUPLE, std::size_t... Is>
auto reverse_tuple(TUPLE &&tuple)
{
return reverse_tuple(std::forward<TUPLE>(tuple),
std::make_index_sequence<std::tuple_size_v<std::decay_t<TUPLE>>>());
}
}


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<int> &values);
Expand Down Expand Up @@ -90,11 +110,23 @@ class LuaCallback
if constexpr (sizeof...(Res) <= 1) {
return (popT<Res>(), ...);
} else {
return std::tuple<Res...>{popT<Res>()...};
return popTs<Res...>(std::make_index_sequence<sizeof...(Res)>());
}
}

explicit operator bool() const { return luastate != nullptr; }

private:
template <typename... Res, std::size_t... Is>
auto popTs(std::index_sequence<Is...>)
{
using RevTuple =
std::tuple<std::tuple_element_t<sizeof...(Is) - 1 - Is, std::tuple<Res...>>...>;
return details::reverse_tuple(std::tuple{popT<std::tuple_element_t<Is, RevTuple>>()...});
}

private:
std::shared_ptr<void> refcounter;
lua_State *luastate;
int luaref;
int arguments;
Expand Down
3 changes: 2 additions & 1 deletion src/stratagus/luacallback.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<void>(nullptr, [this](void*) { luaL_unref(luastate, LUA_REGISTRYINDEX, luaref); });
}

/**
Expand Down Expand Up @@ -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);
}

//@}
136 changes: 136 additions & 0 deletions tests/stratagus/test_luacallback.cpp
Original file line number Diff line number Diff line change
@@ -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 <doctest.h>

#include "stratagus.h"

#include "luacallback.h"
#include "script.h"

namespace doctest
{
template <typename... Ts>
struct StringMaker<std::tuple<Ts...>>
{
static String convert(const std::tuple<Ts...> &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<int>(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<int, bool, int, int>(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<int>(42) == 42);
REQUIRE(g.call<int>(42) == 42);
} // g destructor

REQUIRE(f.call<int>(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<int>(42) == 42);
REQUIRE(g.call<int>(41) == 42);
f = g;
REQUIRE(f.call<int>(41) == 42);
REQUIRE(g.call<int>(41) == 42);
} // g destructor

REQUIRE(f.call<int>(41) == 42);
}

0 comments on commit e9e9337

Please sign in to comment.