Skip to content

Commit

Permalink
Added newFunction to easily wrap callable lambdas (#161)
Browse files Browse the repository at this point in the history
  • Loading branch information
kunitoki authored Dec 31, 2023
1 parent 15cb53a commit d0cc306
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 14 deletions.
4 changes: 2 additions & 2 deletions Source/LuaBridge/detail/ClassInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ namespace detail {
}

template <class T>
[[nodiscard]] static constexpr auto typeName() noexcept
[[nodiscard]] static constexpr auto typeName(T* = nullptr) noexcept
{
constexpr std::string_view prettyName{ LUABRIDGE_PRETTY_FUNCTION };

Expand All @@ -49,7 +49,7 @@ template <class T>
}

template <class T, auto = typeName<T>().find_first_of('.')>
[[nodiscard]] static constexpr auto typeHash() noexcept
[[nodiscard]] static constexpr auto typeHash(T* = nullptr) noexcept
{
constexpr auto stripped = typeName<T>();

Expand Down
37 changes: 37 additions & 0 deletions Source/LuaBridge/detail/LuaRef.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ class LuaRefBase
LuaRefBase(lua_State* L) noexcept
: m_L(L)
{
LUABRIDGE_ASSERT(L != nullptr);
}

//=============================================================================================
Expand Down Expand Up @@ -856,6 +857,8 @@ class LuaRef : public LuaRefBase<LuaRef, LuaRef>
m_ref = luaL_ref(m_L, LUA_REGISTRYINDEX);
}

LuaRef(std::nullptr_t) noexcept = delete;

public:
//=============================================================================================
/**
Expand Down Expand Up @@ -990,6 +993,28 @@ class LuaRef : public LuaRefBase<LuaRef, LuaRef>
lua_newtable(L);
return LuaRef(L, FromStack());
}

//=============================================================================================
/**
* @brief Create a new function on the top of a Lua stack and return a reference to it.
*
* @param L A Lua state.
*
* @returns A reference to the newly created function.
*
* @see luabridge::newFunction()
*/
template <class F>
static LuaRef newFunction(lua_State* L, F&& func)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 1))
return { L };
#endif

detail::push_function(L, std::forward<F>(func));
return LuaRef(L, FromStack());
}

//=============================================================================================
/**
Expand Down Expand Up @@ -1354,6 +1379,18 @@ struct Stack<LuaRef::TableItem>
return LuaRef::newTable(L);
}

//=================================================================================================
/**
* @brief Create a reference to a new function.
*
* This is a syntactic abbreviation for LuaRef::newFunction ().
*/
template <class F>
[[nodiscard]] inline LuaRef newFunction(lua_State* L, F&& func)
{
return LuaRef::newFunction(L, std::forward<F>(func));
}

//=================================================================================================
/**
* @brief Create a reference to a value in the global table.
Expand Down
82 changes: 82 additions & 0 deletions Tests/Source/ClassTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,88 @@ TEST_F(ClassTests, PassRegisteredClassInsteadOfUnregisteredThrows)
ASSERT_TRUE(result().isNil());
}

namespace {
struct NodeX
{
virtual ~NodeX() = default;
};

struct TriggerX : NodeX
{
int trigger() const
{
return 2;
}
};

struct ExplosionX : NodeX
{
int explode() const
{
return 3;
}
};

template <class From, class To>
auto classCast(luabridge::LuaRef from, lua_State* L)
-> std::enable_if_t<std::is_base_of_v<From, To>, To*>
{
auto fromInstance = from.cast<From*>();
if (! fromInstance)
luabridge::raise_lua_error(L, "Impossible to perform the desired cast: %s", fromInstance.message().c_str());

auto toInstance = dynamic_cast<To*>(fromInstance.value());
if (toInstance == nullptr)
luabridge::raise_lua_error(L, "Impossible to perform the desired cast: instance is not of the desired type");

return toInstance;
}
} // namespace

TEST_F(ClassTests, DerivedClassCast)
{
TriggerX triggerInstance;
ExplosionX explosionInstance;

luabridge::getGlobalNamespace(L)
.beginClass<NodeX>("NodeX")
.endClass()
.deriveClass<TriggerX, NodeX>("TriggerX")
.addFunction("trigger", &TriggerX::trigger)
.endClass()
.deriveClass<ExplosionX, NodeX>("ExplosionX")
.addFunction("explode", &ExplosionX::explode)
.endClass()
.addFunction("getNode", [&](int value) -> NodeX*
{
if (value == 0)
return std::addressof(triggerInstance);
else
return std::addressof(explosionInstance);
});

luabridge::LuaRef castTable = luabridge::newTable(L);
castTable[luabridge::getGlobal(L, "TriggerX")] = luabridge::newFunction(L, &classCast<NodeX, TriggerX>);
castTable[luabridge::getGlobal(L, "ExplosionX")] = luabridge::newFunction(L, &classCast<NodeX, ExplosionX>);
luabridge::setGlobal(L, castTable, "cast");

ASSERT_TRUE(runLua("node = getNode (0); result = cast[TriggerX](node):trigger()"));
ASSERT_TRUE(result().isNumber());
EXPECT_EQ(result().cast<int>(), 2);

ASSERT_TRUE(runLua("node = getNode (1); result = cast[ExplosionX](node):explode()"));
ASSERT_TRUE(result().isNumber());
EXPECT_EQ(result().cast<int>(), 3);

#if LUABRIDGE_HAS_EXCEPTIONS
ASSERT_THROW(runLua("node = getNode (1); result = cast[TriggerX](node):trigger()"), std::exception);
ASSERT_THROW(runLua("node = 1; result = cast[TriggerX](node):trigger()"), std::exception);
#else
ASSERT_FALSE(runLua("node = getNode (1); result = cast[TriggerX](node):trigger()"));
ASSERT_FALSE(runLua("node = 1; result = cast[TriggerX](node):trigger()"));
#endif
}

namespace {
Class<int, EmptyBase>& returnRef()
{
Expand Down
52 changes: 40 additions & 12 deletions Tests/Source/LuaRefTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -668,23 +668,51 @@ TEST_F(LuaRefTests, RegisterLambdaInTable)
{
luabridge::getGlobalNamespace(L)
.beginNamespace("Entities")
.addFunction("GetLocalHero", [&]() {
auto table = luabridge::newTable(L);
table.push(L);

luabridge::getNamespaceFromStack(L)
.addProperty("index", [] { return 150; })
.addFunction("Health", [&] { return 500; });

table.pop();
return table;
})
.endNamespace();
.addFunction("GetLocalHero", [&]() {
auto table = luabridge::newTable(L);
table.push(L);
luabridge::getNamespaceFromStack(L)
.addProperty("index", [] { return 150; })
.addFunction("Health", [&] { return 500; });

table.pop();
return table;
})
.endNamespace();

runLua("result = Entities.GetLocalHero().Health()");
ASSERT_EQ(500, result<int>());
}

TEST_F(LuaRefTests, RegisterLambdaInFunction)
{
struct Class
{
Class() = default;

int test() const
{
return 1;
}
};

luabridge::getGlobalNamespace(L)
.beginClass<Class>("Class")
.addConstructor<void (*)()>()
.addFunction("test", &Class::test)
.endClass();

luabridge::setGlobal(L, luabridge::newFunction(L, [](const Class* obj, int x) { return obj->test() + x; }), "takeClass");
luabridge::setGlobal(L, luabridge::newFunction(L, [](Class* obj, int x, int y, lua_State* L) { return obj->test() + x + y + lua_gettop(L); }), "takeClassState");

runLua("obj = Class(); result = takeClass (obj, 10)");
ASSERT_EQ(1 + 10, result<int>());

runLua("obj = Class(); result = takeClassState (obj, 10, 100)");
ASSERT_EQ(1 + 10 + 100 + 3, result<int>());
}

TEST_F(LuaRefTests, HookTesting)
{
std::map<std::string, luabridge::LuaRef> hooklist;
Expand Down

0 comments on commit d0cc306

Please sign in to comment.