diff --git a/Distribution/LuaBridge/LuaBridge.h b/Distribution/LuaBridge/LuaBridge.h index 334eddb3..b0fde0c5 100644 --- a/Distribution/LuaBridge/LuaBridge.h +++ b/Distribution/LuaBridge/LuaBridge.h @@ -6376,7 +6376,8 @@ struct StackOpSelector static ReturnType get(lua_State* L, int index) { return Stack::get(L, index); } - static bool isInstance(lua_State* L, int index) { return Stack::isInstance(L, index); } + template + static bool isInstance(lua_State* L, int index) { return Stack::isInstance(L, index); } }; template @@ -6391,7 +6392,8 @@ struct StackOpSelector static ReturnType get(lua_State* L, int index) { return Stack::get(L, index); } - static bool isInstance(lua_State* L, int index) { return Stack::isInstance(L, index); } + template + static bool isInstance(lua_State* L, int index) { return Stack::isInstance(L, index); } }; template @@ -6403,7 +6405,8 @@ struct StackOpSelector static ReturnType get(lua_State* L, int index) { return Stack::get(L, index); } - static bool isInstance(lua_State* L, int index) { return Stack::isInstance(L, index); } + template + static bool isInstance(lua_State* L, int index) { return Stack::isInstance(L, index); } }; template @@ -6415,7 +6418,8 @@ struct StackOpSelector static ReturnType get(lua_State* L, int index) { return Stack::get(L, index); } - static bool isInstance(lua_State* L, int index) { return Stack::isInstance(L, index); } + template + static bool isInstance(lua_State* L, int index) { return Stack::isInstance(L, index); } }; } diff --git a/Source/LuaBridge/detail/Stack.h b/Source/LuaBridge/detail/Stack.h index b29c9be9..96d32351 100644 --- a/Source/LuaBridge/detail/Stack.h +++ b/Source/LuaBridge/detail/Stack.h @@ -1518,7 +1518,8 @@ struct StackOpSelector static ReturnType get(lua_State* L, int index) { return Stack::get(L, index); } - static bool isInstance(lua_State* L, int index) { return Stack::isInstance(L, index); } + template + static bool isInstance(lua_State* L, int index) { return Stack::isInstance(L, index); } }; template @@ -1533,7 +1534,8 @@ struct StackOpSelector static ReturnType get(lua_State* L, int index) { return Stack::get(L, index); } - static bool isInstance(lua_State* L, int index) { return Stack::isInstance(L, index); } + template + static bool isInstance(lua_State* L, int index) { return Stack::isInstance(L, index); } }; template @@ -1545,7 +1547,8 @@ struct StackOpSelector static ReturnType get(lua_State* L, int index) { return Stack::get(L, index); } - static bool isInstance(lua_State* L, int index) { return Stack::isInstance(L, index); } + template + static bool isInstance(lua_State* L, int index) { return Stack::isInstance(L, index); } }; template @@ -1557,7 +1560,8 @@ struct StackOpSelector static ReturnType get(lua_State* L, int index) { return Stack::get(L, index); } - static bool isInstance(lua_State* L, int index) { return Stack::isInstance(L, index); } + template + static bool isInstance(lua_State* L, int index) { return Stack::isInstance(L, index); } }; } // namespace detail diff --git a/Tests/Source/OverloadTests.cpp b/Tests/Source/OverloadTests.cpp index 5fda0184..b5a27907 100644 --- a/Tests/Source/OverloadTests.cpp +++ b/Tests/Source/OverloadTests.cpp @@ -549,6 +549,51 @@ TEST_F(OverloadTests, OverloadOperatorClass) EXPECT_EQ(6, result2.value); } +TEST_F(OverloadTests, ConstReferenceToNonUserdataParameters) +{ + // Registering an overload set whose parameters are references to + // non-userdata types instantiates Stack::isInstance, which used to + // be a compile error. Arity alone selects the overload here, so the runtime + // expectations stay unambiguous. + struct X + { + int sum(const int& a) const { return a; } + int sum(const int& a, const int& b) const { return a + b; } + }; + + luabridge::getGlobalNamespace(L) + .beginClass("X") + .addConstructor() + .addFunction("sum", + luabridge::constOverload(&X::sum), + luabridge::constOverload(&X::sum)) + .endClass(); + + runLua("x = X(); result = x:sum(3)"); + EXPECT_EQ(3, result()); + + runLua("x = X(); result = x:sum(3, 4)"); + EXPECT_EQ(7, result()); +} + +TEST_F(OverloadTests, ConstReferenceToStringParameters) +{ + luabridge::getGlobalNamespace(L) + .addFunction("test", + [](const std::string& s) -> int { + return static_cast(s.size()); + }, + [](const std::string& s, const int& factor) -> int { + return static_cast(s.size()) * factor; + }); + + runLua("result = test ('abcd')"); + EXPECT_EQ(4, result()); + + runLua("result = test ('abcd', 3)"); + EXPECT_EQ(12, result()); +} + TEST_F(OverloadTests, LuaCFunctionFallback) { struct X diff --git a/Tests/Source/StackTests.cpp b/Tests/Source/StackTests.cpp index f73d088a..f46ab4c0 100644 --- a/Tests/Source/StackTests.cpp +++ b/Tests/Source/StackTests.cpp @@ -32,6 +32,53 @@ TEST_F(StackTests, VoidStackOverflow) ASSERT_TRUE(luabridge::Stack::push(L)); } +TEST_F(StackTests, ReferenceAndPointerToNonUserdataTypes) +{ + // Stack, Stack, Stack and Stack all forward to + // `Helper::template isInstance(...)`, which requires the selected + // StackOpSelector to declare isInstance as a template. The non-userdata + // specialisations declared it as a plain static function, so instantiating + // isInstance for a reference or pointer to any non-userdata type was a hard + // compile error ("'isInstance' following the 'template' keyword does not + // refer to a template"). That made luabridge::overload(...) + // unusable for int, double, std::string and every other non-userdata type. + { + ASSERT_TRUE(luabridge::push(L, 42)); + + EXPECT_TRUE(luabridge::isInstance(L, -1)); + EXPECT_TRUE(luabridge::isInstance(L, -1)); + EXPECT_TRUE(luabridge::isInstance(L, -1)); + EXPECT_TRUE(luabridge::isInstance(L, -1)); + + EXPECT_TRUE(luabridge::isInstance(L, -1)); + EXPECT_TRUE(luabridge::isInstance(L, -1)); + + lua_pop(L, 1); + } + + { + ASSERT_TRUE(luabridge::push(L, std::string("abc"))); + + EXPECT_TRUE(luabridge::isInstance(L, -1)); + EXPECT_TRUE(luabridge::isInstance(L, -1)); + + EXPECT_FALSE(luabridge::isInstance(L, -1)); + EXPECT_FALSE(luabridge::isInstance(L, -1)); + + // The userdata specialisations reach isInstance through the same path + // and must keep working. + EXPECT_FALSE(luabridge::isInstance(L, -1)); + EXPECT_FALSE(luabridge::isInstance(L, -1)); + + // An explicit U is forwarded rather than silently replaced by T, which + // mirrors how the userdata selectors forward Userdata::isInstance. + EXPECT_TRUE( + (luabridge::detail::StackOpSelector::isInstance(L, -1))); + + lua_pop(L, 1); + } +} + TEST_F(StackTests, NullptrType) { {