Skip to content

Commit

Permalink
Put some order to addProperty, also added __destruct metamethod for n…
Browse files Browse the repository at this point in the history
…on luau
  • Loading branch information
kunitoki committed Sep 29, 2024
1 parent 5f49013 commit 713c1f5
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 7 deletions.
83 changes: 81 additions & 2 deletions Source/LuaBridge/detail/CFunctions.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#pragma once

#include "Config.h"
#include "ClassInfo.h"
#include "Errors.h"
#include "FuncTraits.h"
#include "LuaHelpers.h"
Expand Down Expand Up @@ -577,7 +578,7 @@ inline int read_only_error(lua_State* L)
* @brief __tostring metamethod for a class.
*/
template <class C>
static int tostring_metamethod(lua_State* L)
int tostring_metamethod(lua_State* L)
{
const void* ptr = lua_topointer(L, 1);

Expand All @@ -593,13 +594,46 @@ static int tostring_metamethod(lua_State* L)
return 1;
}

//=================================================================================================
/**
* @brief __destruct metamethod for a class.
*/
template <class C>
int destruct_metamethod(lua_State* L)
{
LUABRIDGE_ASSERT(lua_isuserdata(L, 1)); // Stack: userdata (ud)
const auto top = lua_gettop(L);

const int result = lua_getmetatable(L, 1); // Stack: ud, object metatable (ot) | nothing
if (result == 0)
return 0;

LUABRIDGE_ASSERT(lua_istable(L, -1)); // Stack: ud, ot

lua_rawgetp(L, LUA_REGISTRYINDEX, detail::getClassRegistryKey<C>()); // Stack: ud, ot, registry metatable (rt) | nil
if (lua_istable(L, -1)) // Stack: ud, ot, rt
{
rawgetfield(L, -1, "__destruct"); // Stack: ud, ot, rt, ud, function | nil
if (lua_isfunction(L, -1))
{
lua_pushvalue(L, 1); // Stack: ud, ot, rt, function, ud
lua_pcall(L, 1, 0, 0); // Stack: ud, ot, rt
}
}

lua_settop(L, top); // Stack: ud
return 0;
}

//=================================================================================================
/**
* @brief __gc metamethod for a class.
*/
template <class C>
static int gc_metamethod(lua_State* L)
int gc_metamethod(lua_State* L)
{
destruct_metamethod<C>(L);

Userdata* ud = Userdata::getExact<C>(L, 1);
LUABRIDGE_ASSERT(ud);

Expand Down Expand Up @@ -1064,6 +1098,26 @@ int invoke_proxy_constructor(lua_State* L)
return 1;
}

//=================================================================================================
/**
* @brief lua_CFunction to call on a object constructor via functor (lambda wrapped in a std::function).
*
* The proxy std::function (lightuserdata) is in the first upvalue. The class userdata object will be pushed at the top of the Lua stack.
*/
template <class F>
int invoke_proxy_destructor(lua_State* L)
{
using FnTraits = function_traits<F>;

LUABRIDGE_ASSERT(isfulluserdata(L, lua_upvalueindex(1)));

auto& func = *align<F>(lua_touserdata(L, lua_upvalueindex(1)));

function<void, typename FnTraits::argument_types, 1>::call(L, func);

return 1;
}

//=================================================================================================
/**
* @brief lua_CFunction to resolve an invocation between several overloads.
Expand Down Expand Up @@ -1887,6 +1941,31 @@ struct constructor_forwarder
F m_func;
};

//=================================================================================================
/**
* @brief Constructor forwarder.
*/
template <class T, class F>
struct destructor_forwarder
{
explicit destructor_forwarder(F f)
: m_func(std::move(f))
{
}

void operator()(lua_State* L)
{
auto* value = Userdata::get<T>(L, -1, false);
if (value == nullptr)
raise_lua_error(L, "invalid object destruction");

std::invoke(m_func, value);
}

private:
F m_func;
};

//=================================================================================================
/**
* @brief Constructor forwarder.
Expand Down
20 changes: 20 additions & 0 deletions Source/LuaBridge/detail/Namespace.h
Original file line number Diff line number Diff line change
Expand Up @@ -932,6 +932,26 @@ class Namespace : public detail::Registrar
return *this;
}

//=========================================================================================
template <class Function>
auto addDestructor(Function function)
-> std::enable_if_t<detail::is_callable_v<Function>, Class<T>&>
{
static_assert(detail::function_arity_excluding_v<Function, lua_State*> == 1);
static_assert(std::is_same_v<detail::function_argument_t<0, Function>, T*>);

assertStackState(); // Stack: const table (co), class table (cl), static table (st)

using F = detail::destructor_forwarder<T, Function>;

lua_newuserdata_aligned<F>(L, F(std::move(function))); // Stack: co, cl, st, upvalue
lua_pushcclosure_x(L, &detail::invoke_proxy_destructor<F>, className, 1); // Stack: co, cl, st, function

rawsetfield(L, -3, "__destruct"); // Stack: co, cl, st

return *this;
}

//=========================================================================================
/**
* @brief Add or replace a factory.
Expand Down
28 changes: 28 additions & 0 deletions Tests/Source/ClassTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,34 @@ struct ClassFunctions : ClassTests
{
};

#if !LUABRIDGE_ON_LUAU
TEST_F(ClassFunctions, Destructor)
{
using Int = Class<int, EmptyBase>;

bool called = false;
int data = 0;

luabridge::getGlobalNamespace(L)
.beginClass<Int>("Int")
.addConstructor<void(int)>()
.addDestructor([&](Int* obj)
{
called = true;
data = obj->data;
})
.endClass();

runLua("local x = Int(42)");
ASSERT_TRUE(result().isNil());

closeLuaState();

EXPECT_TRUE(called);
EXPECT_EQ(42, data);
}
#endif

TEST_F(ClassFunctions, MemberFunctions)
{
using Int = Class<int, EmptyBase>;
Expand Down
2 changes: 1 addition & 1 deletion Tests/Source/IssueTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ TEST_F(IssueTests, IssueMainThread)
.beginClass<SomeClass>("SomeClass")
.addConstructor<void (*)(lua_State*)>()
.addFunction("SomeMember", &SomeClass::SomeMember)
.addProperty("SomeMemberOverride", &SomeClass::override_)
.addProperty("SomeMemberOverride", &SomeClass::override_, &SomeClass::override_)
.endClass();

const char* source = R"(
Expand Down
4 changes: 2 additions & 2 deletions Tests/Source/LegacyTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -237,10 +237,10 @@ void addToState(lua_State* L)
.addFunction("getName", &A::getName)
.addFunction("testSucceeded", &A::testSucceeded)
.addFunction("__add", &A::operator+)
.addProperty("testProp", &A::testProp)
.addProperty("testProp", &A::testProp, &A::testProp)
.addProperty("testProp2", &A::testPropGet, &A::testPropSet)
.addStaticFunction("testStatic", &A::testStatic)
.addStaticProperty("testStaticProp", &A::testStaticProp)
.addStaticProperty("testStaticProp", &A::testStaticProp, &A::testStaticProp)
.addStaticProperty("testStaticProp2", &A::testStaticPropGet, &A::testStaticPropSet)
.endClass()
.deriveClass<B, A>("B")
Expand Down
4 changes: 2 additions & 2 deletions Tests/Source/Tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1005,8 +1005,8 @@ TEST_F(LuaBridgeTest, Exception)

luabridge::getGlobalNamespace(L)
.beginNamespace("ns")
.addProperty("cb1", &cb1)
.addProperty("cb2", &cb2)
.addProperty("cb1", &cb1, &cb1)
.addProperty("cb2", &cb2, &cb2)
.endNamespace();

auto text = R"(
Expand Down

0 comments on commit 713c1f5

Please sign in to comment.