Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions Distribution/LuaBridge/LuaBridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -6376,7 +6376,8 @@ struct StackOpSelector<T*, false>

static ReturnType get(lua_State* L, int index) { return Stack<T>::get(L, index); }

static bool isInstance(lua_State* L, int index) { return Stack<T>::isInstance(L, index); }
template <class U = T>
static bool isInstance(lua_State* L, int index) { return Stack<U>::isInstance(L, index); }
};

template <class T>
Expand All @@ -6391,7 +6392,8 @@ struct StackOpSelector<const T*, false>

static ReturnType get(lua_State* L, int index) { return Stack<T>::get(L, index); }

static bool isInstance(lua_State* L, int index) { return Stack<T>::isInstance(L, index); }
template <class U = T>
static bool isInstance(lua_State* L, int index) { return Stack<U>::isInstance(L, index); }
};

template <class T>
Expand All @@ -6403,7 +6405,8 @@ struct StackOpSelector<T&, false>

static ReturnType get(lua_State* L, int index) { return Stack<T>::get(L, index); }

static bool isInstance(lua_State* L, int index) { return Stack<T>::isInstance(L, index); }
template <class U = T>
static bool isInstance(lua_State* L, int index) { return Stack<U>::isInstance(L, index); }
};

template <class T>
Expand All @@ -6415,7 +6418,8 @@ struct StackOpSelector<const T&, false>

static ReturnType get(lua_State* L, int index) { return Stack<T>::get(L, index); }

static bool isInstance(lua_State* L, int index) { return Stack<T>::isInstance(L, index); }
template <class U = T>
static bool isInstance(lua_State* L, int index) { return Stack<U>::isInstance(L, index); }
};

}
Expand Down
12 changes: 8 additions & 4 deletions Source/LuaBridge/detail/Stack.h
Original file line number Diff line number Diff line change
Expand Up @@ -1518,7 +1518,8 @@ struct StackOpSelector<T*, false>

static ReturnType get(lua_State* L, int index) { return Stack<T>::get(L, index); }

static bool isInstance(lua_State* L, int index) { return Stack<T>::isInstance(L, index); }
template <class U = T>
static bool isInstance(lua_State* L, int index) { return Stack<U>::isInstance(L, index); }
};

template <class T>
Expand All @@ -1533,7 +1534,8 @@ struct StackOpSelector<const T*, false>

static ReturnType get(lua_State* L, int index) { return Stack<T>::get(L, index); }

static bool isInstance(lua_State* L, int index) { return Stack<T>::isInstance(L, index); }
template <class U = T>
static bool isInstance(lua_State* L, int index) { return Stack<U>::isInstance(L, index); }
};

template <class T>
Expand All @@ -1545,7 +1547,8 @@ struct StackOpSelector<T&, false>

static ReturnType get(lua_State* L, int index) { return Stack<T>::get(L, index); }

static bool isInstance(lua_State* L, int index) { return Stack<T>::isInstance(L, index); }
template <class U = T>
static bool isInstance(lua_State* L, int index) { return Stack<U>::isInstance(L, index); }
};

template <class T>
Expand All @@ -1557,7 +1560,8 @@ struct StackOpSelector<const T&, false>

static ReturnType get(lua_State* L, int index) { return Stack<T>::get(L, index); }

static bool isInstance(lua_State* L, int index) { return Stack<T>::isInstance(L, index); }
template <class U = T>
static bool isInstance(lua_State* L, int index) { return Stack<U>::isInstance(L, index); }
};

} // namespace detail
Expand Down
45 changes: 45 additions & 0 deletions Tests/Source/OverloadTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<const T&>::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>("X")
.addConstructor<void()>()
.addFunction("sum",
luabridge::constOverload<const int&>(&X::sum),
luabridge::constOverload<const int&, const int&>(&X::sum))
.endClass();

runLua("x = X(); result = x:sum(3)");
EXPECT_EQ(3, result<int>());

runLua("x = X(); result = x:sum(3, 4)");
EXPECT_EQ(7, result<int>());
}

TEST_F(OverloadTests, ConstReferenceToStringParameters)
{
luabridge::getGlobalNamespace(L)
.addFunction("test",
[](const std::string& s) -> int {
return static_cast<int>(s.size());
},
[](const std::string& s, const int& factor) -> int {
return static_cast<int>(s.size()) * factor;
});

runLua("result = test ('abcd')");
EXPECT_EQ(4, result<int>());

runLua("result = test ('abcd', 3)");
EXPECT_EQ(12, result<int>());
}

TEST_F(OverloadTests, LuaCFunctionFallback)
{
struct X
Expand Down
47 changes: 47 additions & 0 deletions Tests/Source/StackTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,53 @@ TEST_F(StackTests, VoidStackOverflow)
ASSERT_TRUE(luabridge::Stack<void>::push(L));
}

TEST_F(StackTests, ReferenceAndPointerToNonUserdataTypes)
{
// Stack<T&>, Stack<const T&>, Stack<T*> and Stack<const T*> all forward to
// `Helper::template isInstance<T>(...)`, 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<const T&>(...)
// unusable for int, double, std::string and every other non-userdata type.
{
ASSERT_TRUE(luabridge::push(L, 42));

EXPECT_TRUE(luabridge::isInstance<int&>(L, -1));
EXPECT_TRUE(luabridge::isInstance<const int&>(L, -1));
EXPECT_TRUE(luabridge::isInstance<int*>(L, -1));
EXPECT_TRUE(luabridge::isInstance<const int*>(L, -1));

EXPECT_TRUE(luabridge::isInstance<double&>(L, -1));
EXPECT_TRUE(luabridge::isInstance<const double&>(L, -1));

lua_pop(L, 1);
}

{
ASSERT_TRUE(luabridge::push(L, std::string("abc")));

EXPECT_TRUE(luabridge::isInstance<std::string&>(L, -1));
EXPECT_TRUE(luabridge::isInstance<const std::string&>(L, -1));

EXPECT_FALSE(luabridge::isInstance<int&>(L, -1));
EXPECT_FALSE(luabridge::isInstance<const int&>(L, -1));

// The userdata specialisations reach isInstance through the same path
// and must keep working.
EXPECT_FALSE(luabridge::isInstance<Unregistered&>(L, -1));
EXPECT_FALSE(luabridge::isInstance<const Unregistered&>(L, -1));

// An explicit U is forwarded rather than silently replaced by T, which
// mirrors how the userdata selectors forward Userdata::isInstance<U>.
EXPECT_TRUE(
(luabridge::detail::StackOpSelector<const int&, false>::isInstance<std::string>(L, -1)));

lua_pop(L, 1);
}
}

TEST_F(StackTests, NullptrType)
{
{
Expand Down
Loading