Skip to content

Commit 29ff247

Browse files
authored
Fix non userdata const reference overloads (#270)
1 parent eaafdf0 commit 29ff247

4 files changed

Lines changed: 108 additions & 8 deletions

File tree

‎Distribution/LuaBridge/LuaBridge.h‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6376,7 +6376,8 @@ struct StackOpSelector<T*, false>
63766376

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

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

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

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

6394-
static bool isInstance(lua_State* L, int index) { return Stack<T>::isInstance(L, index); }
6395+
template <class U = T>
6396+
static bool isInstance(lua_State* L, int index) { return Stack<U>::isInstance(L, index); }
63956397
};
63966398

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

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

6406-
static bool isInstance(lua_State* L, int index) { return Stack<T>::isInstance(L, index); }
6408+
template <class U = T>
6409+
static bool isInstance(lua_State* L, int index) { return Stack<U>::isInstance(L, index); }
64076410
};
64086411

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

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

6418-
static bool isInstance(lua_State* L, int index) { return Stack<T>::isInstance(L, index); }
6421+
template <class U = T>
6422+
static bool isInstance(lua_State* L, int index) { return Stack<U>::isInstance(L, index); }
64196423
};
64206424

64216425
}

‎Source/LuaBridge/detail/Stack.h‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1518,7 +1518,8 @@ struct StackOpSelector<T*, false>
15181518

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

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

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

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

1536-
static bool isInstance(lua_State* L, int index) { return Stack<T>::isInstance(L, index); }
1537+
template <class U = T>
1538+
static bool isInstance(lua_State* L, int index) { return Stack<U>::isInstance(L, index); }
15371539
};
15381540

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

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

1548-
static bool isInstance(lua_State* L, int index) { return Stack<T>::isInstance(L, index); }
1550+
template <class U = T>
1551+
static bool isInstance(lua_State* L, int index) { return Stack<U>::isInstance(L, index); }
15491552
};
15501553

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

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

1560-
static bool isInstance(lua_State* L, int index) { return Stack<T>::isInstance(L, index); }
1563+
template <class U = T>
1564+
static bool isInstance(lua_State* L, int index) { return Stack<U>::isInstance(L, index); }
15611565
};
15621566

15631567
} // namespace detail

‎Tests/Source/OverloadTests.cpp‎

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,51 @@ TEST_F(OverloadTests, OverloadOperatorClass)
549549
EXPECT_EQ(6, result2.value);
550550
}
551551

552+
TEST_F(OverloadTests, ConstReferenceToNonUserdataParameters)
553+
{
554+
// Registering an overload set whose parameters are references to
555+
// non-userdata types instantiates Stack<const T&>::isInstance, which used to
556+
// be a compile error. Arity alone selects the overload here, so the runtime
557+
// expectations stay unambiguous.
558+
struct X
559+
{
560+
int sum(const int& a) const { return a; }
561+
int sum(const int& a, const int& b) const { return a + b; }
562+
};
563+
564+
luabridge::getGlobalNamespace(L)
565+
.beginClass<X>("X")
566+
.addConstructor<void()>()
567+
.addFunction("sum",
568+
luabridge::constOverload<const int&>(&X::sum),
569+
luabridge::constOverload<const int&, const int&>(&X::sum))
570+
.endClass();
571+
572+
runLua("x = X(); result = x:sum(3)");
573+
EXPECT_EQ(3, result<int>());
574+
575+
runLua("x = X(); result = x:sum(3, 4)");
576+
EXPECT_EQ(7, result<int>());
577+
}
578+
579+
TEST_F(OverloadTests, ConstReferenceToStringParameters)
580+
{
581+
luabridge::getGlobalNamespace(L)
582+
.addFunction("test",
583+
[](const std::string& s) -> int {
584+
return static_cast<int>(s.size());
585+
},
586+
[](const std::string& s, const int& factor) -> int {
587+
return static_cast<int>(s.size()) * factor;
588+
});
589+
590+
runLua("result = test ('abcd')");
591+
EXPECT_EQ(4, result<int>());
592+
593+
runLua("result = test ('abcd', 3)");
594+
EXPECT_EQ(12, result<int>());
595+
}
596+
552597
TEST_F(OverloadTests, LuaCFunctionFallback)
553598
{
554599
struct X

‎Tests/Source/StackTests.cpp‎

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,53 @@ TEST_F(StackTests, VoidStackOverflow)
3232
ASSERT_TRUE(luabridge::Stack<void>::push(L));
3333
}
3434

35+
TEST_F(StackTests, ReferenceAndPointerToNonUserdataTypes)
36+
{
37+
// Stack<T&>, Stack<const T&>, Stack<T*> and Stack<const T*> all forward to
38+
// `Helper::template isInstance<T>(...)`, which requires the selected
39+
// StackOpSelector to declare isInstance as a template. The non-userdata
40+
// specialisations declared it as a plain static function, so instantiating
41+
// isInstance for a reference or pointer to any non-userdata type was a hard
42+
// compile error ("'isInstance' following the 'template' keyword does not
43+
// refer to a template"). That made luabridge::overload<const T&>(...)
44+
// unusable for int, double, std::string and every other non-userdata type.
45+
{
46+
ASSERT_TRUE(luabridge::push(L, 42));
47+
48+
EXPECT_TRUE(luabridge::isInstance<int&>(L, -1));
49+
EXPECT_TRUE(luabridge::isInstance<const int&>(L, -1));
50+
EXPECT_TRUE(luabridge::isInstance<int*>(L, -1));
51+
EXPECT_TRUE(luabridge::isInstance<const int*>(L, -1));
52+
53+
EXPECT_TRUE(luabridge::isInstance<double&>(L, -1));
54+
EXPECT_TRUE(luabridge::isInstance<const double&>(L, -1));
55+
56+
lua_pop(L, 1);
57+
}
58+
59+
{
60+
ASSERT_TRUE(luabridge::push(L, std::string("abc")));
61+
62+
EXPECT_TRUE(luabridge::isInstance<std::string&>(L, -1));
63+
EXPECT_TRUE(luabridge::isInstance<const std::string&>(L, -1));
64+
65+
EXPECT_FALSE(luabridge::isInstance<int&>(L, -1));
66+
EXPECT_FALSE(luabridge::isInstance<const int&>(L, -1));
67+
68+
// The userdata specialisations reach isInstance through the same path
69+
// and must keep working.
70+
EXPECT_FALSE(luabridge::isInstance<Unregistered&>(L, -1));
71+
EXPECT_FALSE(luabridge::isInstance<const Unregistered&>(L, -1));
72+
73+
// An explicit U is forwarded rather than silently replaced by T, which
74+
// mirrors how the userdata selectors forward Userdata::isInstance<U>.
75+
EXPECT_TRUE(
76+
(luabridge::detail::StackOpSelector<const int&, false>::isInstance<std::string>(L, -1)));
77+
78+
lua_pop(L, 1);
79+
}
80+
}
81+
3582
TEST_F(StackTests, NullptrType)
3683
{
3784
{

0 commit comments

Comments
 (0)