Skip to content

Commit 93c85d3

Browse files
authored
Added test to check for error handlers (#214)
1 parent 03bd9ad commit 93c85d3

File tree

3 files changed

+152
-8
lines changed

3 files changed

+152
-8
lines changed

Distribution/LuaBridge/LuaBridge.h

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9595,6 +9595,17 @@ namespace luabridge {
95959595

95969596
namespace detail {
95979597

9598+
template <class F>
9599+
bool is_handler_valid(const F& f) noexcept
9600+
{
9601+
if constexpr (std::is_pointer_v<remove_cvref_t<F>>)
9602+
return f != nullptr;
9603+
else if constexpr (std::is_constructible_v<bool, remove_cvref_t<F>>)
9604+
return static_cast<bool>(f);
9605+
else
9606+
return true;
9607+
}
9608+
95989609
template <class T>
95999610
struct IsTuple : std::false_type
96009611
{
@@ -9667,14 +9678,22 @@ TypeResult<R> decodeCallResult(lua_State* L, int firstResultIndex, int numReturn
96679678
template <class R, class Ref, class F, class... Args>
96689679
TypeResult<R> callWithHandler(const Ref& object, F&& errorHandler, Args&&... args)
96699680
{
9670-
static constexpr bool isValidHandler = !std::is_same_v<detail::remove_cvref_t<F>, detail::remove_cvref_t<decltype(std::ignore)>>;
9681+
static_assert(std::is_same_v<detail::remove_cvref_t<F>, detail::remove_cvref_t<decltype(std::ignore)>> || std::is_invocable_r_v<int, F, lua_State*>);
9682+
9683+
static constexpr bool isValidHandler =
9684+
!std::is_same_v<detail::remove_cvref_t<F>, detail::remove_cvref_t<decltype(std::ignore)>>;
96719685

96729686
lua_State* L = object.state();
96739687
const StackRestore stackRestore(L);
96749688
const int initialTop = lua_gettop(L);
96759689

9690+
bool hasHandler = false;
96769691
if constexpr (isValidHandler)
9677-
detail::push_function(L, std::forward<F>(errorHandler), "");
9692+
{
9693+
hasHandler = detail::is_handler_valid(errorHandler);
9694+
if (hasHandler)
9695+
detail::push_function(L, std::forward<F>(errorHandler), "");
9696+
}
96789697

96799698
object.push();
96809699

@@ -9684,7 +9703,7 @@ TypeResult<R> callWithHandler(const Ref& object, F&& errorHandler, Args&&... arg
96849703
return result.error();
96859704
}
96869705

9687-
const int messageHandlerIndex = isValidHandler ? (initialTop + 1) : 0;
9706+
const int messageHandlerIndex = hasHandler ? (initialTop + 1) : 0;
96889707
const int code = lua_pcall(L, sizeof...(Args), LUA_MULTRET, messageHandlerIndex);
96899708
if (code != LUABRIDGE_LUA_OK)
96909709
{
@@ -9702,7 +9721,7 @@ TypeResult<R> callWithHandler(const Ref& object, F&& errorHandler, Args&&... arg
97029721
return ec;
97039722
}
97049723

9705-
if constexpr (isValidHandler)
9724+
if (hasHandler)
97069725
lua_remove(L, initialTop + 1);
97079726

97089727
const int firstResultIndex = initialTop + 1;

Source/LuaBridge/detail/Invoke.h

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,17 @@ namespace luabridge {
2020
//=================================================================================================
2121
namespace detail {
2222

23+
template <class F>
24+
bool is_handler_valid(const F& f) noexcept
25+
{
26+
if constexpr (std::is_pointer_v<remove_cvref_t<F>>)
27+
return f != nullptr;
28+
else if constexpr (std::is_constructible_v<bool, remove_cvref_t<F>>)
29+
return static_cast<bool>(f);
30+
else
31+
return true;
32+
}
33+
2334
template <class T>
2435
struct IsTuple : std::false_type
2536
{
@@ -96,14 +107,22 @@ TypeResult<R> decodeCallResult(lua_State* L, int firstResultIndex, int numReturn
96107
template <class R, class Ref, class F, class... Args>
97108
TypeResult<R> callWithHandler(const Ref& object, F&& errorHandler, Args&&... args)
98109
{
99-
static constexpr bool isValidHandler = !std::is_same_v<detail::remove_cvref_t<F>, detail::remove_cvref_t<decltype(std::ignore)>>;
110+
static_assert(std::is_same_v<detail::remove_cvref_t<F>, detail::remove_cvref_t<decltype(std::ignore)>> || std::is_invocable_r_v<int, F, lua_State*>);
111+
112+
static constexpr bool isValidHandler =
113+
!std::is_same_v<detail::remove_cvref_t<F>, detail::remove_cvref_t<decltype(std::ignore)>>;
100114

101115
lua_State* L = object.state();
102116
const StackRestore stackRestore(L);
103117
const int initialTop = lua_gettop(L);
104118

119+
bool hasHandler = false;
105120
if constexpr (isValidHandler)
106-
detail::push_function(L, std::forward<F>(errorHandler), "");
121+
{
122+
hasHandler = detail::is_handler_valid(errorHandler);
123+
if (hasHandler)
124+
detail::push_function(L, std::forward<F>(errorHandler), "");
125+
}
107126

108127
object.push();
109128

@@ -113,7 +132,7 @@ TypeResult<R> callWithHandler(const Ref& object, F&& errorHandler, Args&&... arg
113132
return result.error();
114133
}
115134

116-
const int messageHandlerIndex = isValidHandler ? (initialTop + 1) : 0;
135+
const int messageHandlerIndex = hasHandler ? (initialTop + 1) : 0;
117136
const int code = lua_pcall(L, sizeof...(Args), LUA_MULTRET, messageHandlerIndex);
118137
if (code != LUABRIDGE_LUA_OK)
119138
{
@@ -131,7 +150,7 @@ TypeResult<R> callWithHandler(const Ref& object, F&& errorHandler, Args&&... arg
131150
return ec;
132151
}
133152

134-
if constexpr (isValidHandler)
153+
if (hasHandler)
135154
lua_remove(L, initialTop + 1);
136155

137156
const int firstResultIndex = initialTop + 1;

Tests/Source/LuaRefTests.cpp

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,112 @@ TEST_F(LuaRefTests, CallableWithHandler)
533533
EXPECT_TRUE(errorMessage.find("we failed badly") != std::string::npos);
534534
}
535535

536+
TEST_F(LuaRefTests, CallableWithHandlerAsIntToBoolValuedFunction)
537+
{
538+
runLua("function f(x) return x <= 1 end");
539+
auto f = luabridge::getGlobal(L, "f");
540+
EXPECT_TRUE(f.isCallable());
541+
542+
bool calledHandler = false;
543+
std::string errorMessage;
544+
auto handler = [&](lua_State*) -> int
545+
{
546+
calledHandler = true;
547+
548+
if (auto msg = lua_tostring(L, 1))
549+
errorMessage = msg;
550+
551+
return 0;
552+
};
553+
554+
auto result = f.callWithHandler<bool>(handler, 2);
555+
ASSERT_TRUE(result);
556+
EXPECT_FALSE(calledHandler);
557+
EXPECT_FALSE(result.value());
558+
}
559+
560+
TEST_F(LuaRefTests, CallableWithStdFunction)
561+
{
562+
runLua("function f(x) error('we failed ' .. x) end");
563+
auto f = luabridge::getGlobal(L, "f");
564+
EXPECT_TRUE(f.isCallable());
565+
566+
bool calledHandler = false;
567+
std::string errorMessage;
568+
auto handler = [&](lua_State*) -> int
569+
{
570+
calledHandler = true;
571+
572+
if (auto msg = lua_tostring(L, 1))
573+
errorMessage = msg;
574+
575+
return 0;
576+
};
577+
578+
std::function<int(lua_State*)> pHandler = handler;
579+
580+
EXPECT_FALSE(f.callWithHandler(pHandler, "badly"));
581+
EXPECT_TRUE(calledHandler);
582+
EXPECT_TRUE(errorMessage.find("we failed badly") != std::string::npos);
583+
}
584+
585+
TEST_F(LuaRefTests, CallableWithNullifiedStdFunction)
586+
{
587+
runLua("function f(x) error('we failed ' .. x) end");
588+
auto f = luabridge::getGlobal(L, "f");
589+
EXPECT_TRUE(f.isCallable());
590+
591+
std::function<int(lua_State*)> pHandler = nullptr;
592+
EXPECT_FALSE(f.callWithHandler(pHandler, "badly"));
593+
594+
#if LUABRIDGE_HAS_EXCEPTIONS
595+
EXPECT_ANY_THROW(f.callWithHandler(pHandler, "badly").throw_on_error());
596+
#endif
597+
}
598+
599+
TEST_F(LuaRefTests, CallableWithCFunction)
600+
{
601+
runLua("function f(x) error('we failed ' .. x) end");
602+
auto f = luabridge::getGlobal(L, "f");
603+
EXPECT_TRUE(f.isCallable());
604+
605+
lua_CFunction pHandler = +[](lua_State* L) { return 0; };
606+
EXPECT_FALSE(f.callWithHandler(pHandler, "badly"));
607+
}
608+
609+
TEST_F(LuaRefTests, CallableWithNullCFunction)
610+
{
611+
runLua("function f(x) error('we failed ' .. x) end");
612+
auto f = luabridge::getGlobal(L, "f");
613+
EXPECT_TRUE(f.isCallable());
614+
615+
lua_CFunction pHandler = nullptr;
616+
EXPECT_FALSE(f.callWithHandler(pHandler, "badly"));
617+
618+
#if LUABRIDGE_HAS_EXCEPTIONS
619+
EXPECT_ANY_THROW(f.callWithHandler(pHandler, "badly").throw_on_error());
620+
#endif
621+
}
622+
623+
#if LUABRIDGE_HAS_EXCEPTIONS
624+
TEST_F(LuaRefTests, CallableWithThrowingHandler)
625+
{
626+
runLua("function f(x) error('we failed ' .. x) end");
627+
auto f = luabridge::getGlobal(L, "f");
628+
EXPECT_TRUE(f.isCallable());
629+
630+
bool calledHandler = false;
631+
auto handler = [&](lua_State*) -> int
632+
{
633+
calledHandler = true;
634+
return 0;
635+
};
636+
637+
EXPECT_ANY_THROW(f.callWithHandler(handler, "badly").throw_on_error());
638+
EXPECT_TRUE(calledHandler);
639+
}
640+
#endif
641+
536642
TEST_F(LuaRefTests, CallableWrapper)
537643
{
538644
runLua("function sum(a, b) return a + b end");

0 commit comments

Comments
 (0)