#define SOL_CHECK_ARGUMENTS #include #include #include #include "test_stack_guard.hpp" template T va_func(sol::variadic_args va, T first) { T s = 0; for (auto arg : va) { T v = arg; s += v; } std::cout << first << std::endl; std::cout << s << std::endl; return s; } std::function makefn() { auto fx = []() -> int { return 0x1456789; }; return fx; } void takefn(std::function purr) { if (purr() != 0x1456789) throw 0; } struct A { int a = 0xA; int bark() { return 1; } }; std::tuple bark(int num_value, A* a) { return std::tuple(num_value * 2, a->bark()); } void test_free_func(std::function f) { f(); } void test_free_func2(std::function f, int arg1) { int val = f(arg1); if (val != arg1) throw sol::error("failed function call!"); } int overloaded(int x) { INFO(x); return 3; } int overloaded(int x, int y) { INFO(x << " " << y); return 7; } int overloaded(int x, int y, int z) { INFO(x << " " << y << " " << z); return 11; } int non_overloaded(int x, int y, int z) { INFO(x << " " << y << " " << z); return 13; } namespace sep { int plop_xyz(int x, int y, std::string z) { INFO(x << " " << y << " " << z); return 11; } } int func_1(int) { return 1; } std::string func_1s(std::string a) { return "string: " + a; } int func_2(int, int) { return 2; } void func_3(int, int, int) { } int f1(int) { return 32; } int f2(int, int) { return 1; } struct fer { double f3(int, int) { return 2.5; } }; static int raw_noexcept_function(lua_State* L) noexcept { return sol::stack::push(L, 0x63); } TEST_CASE("functions/tuple returns", "Make sure tuple returns are ordered properly") { sol::state lua; lua.script("function f() return '3', 4 end"); std::tuple result = lua["f"](); auto s = std::get<0>(result); auto v = std::get<1>(result); REQUIRE(s == "3"); REQUIRE(v == 4); } TEST_CASE("functions/overload resolution", "Check if overloaded function resolution templates compile/work") { sol::state lua; lua.open_libraries(sol::lib::base); lua.set_function("non_overloaded", non_overloaded); REQUIRE_NOTHROW(lua.script("x = non_overloaded(1, 2, 3)\nprint(x)")); /* // Cannot reasonably support: clang++ refuses to try enough // deductions to make this work lua.set_function("overloaded", overloaded); REQUIRE_NOTHROW(lua.script("print(overloaded(1))")); lua.set_function("overloaded", overloaded); REQUIRE_NOTHROW(lua.script("print(overloaded(1, 2))")); lua.set_function("overloaded", overloaded); REQUIRE_NOTHROW(lua.script("print(overloaded(1, 2, 3))")); */ lua.set_function("overloaded", sol::resolve(overloaded)); REQUIRE_NOTHROW(lua.script("print(overloaded(1))")); lua.set_function("overloaded", sol::resolve(overloaded)); REQUIRE_NOTHROW(lua.script("print(overloaded(1, 2))")); lua.set_function("overloaded", sol::resolve(overloaded)); REQUIRE_NOTHROW(lua.script("print(overloaded(1, 2, 3))")); } TEST_CASE("functions/return order and multi get", "Check if return order is in the same reading order specified in Lua") { const static std::tuple triple = std::make_tuple(10, 11, 12); const static std::tuple paired = std::make_tuple(10, 10.f); sol::state lua; lua.set_function("f", [] { return std::make_tuple(10, 11, 12); }); int a = 0; lua.set_function("h", []() { return std::make_tuple(10, 10.0f); }); lua.script("function g() return 10, 11, 12 end\nx,y,z = g()"); auto tcpp = lua.get("f").call(); auto tlua = lua.get("g").call(); auto tcpp2 = lua.get("h").call(); auto tluaget = lua.get("x", "y", "z"); REQUIRE(tcpp == triple); REQUIRE(tlua == triple); REQUIRE(tluaget == triple); REQUIRE(tcpp2 == paired); } TEST_CASE("functions/deducing return order and multi get", "Check if return order is in the same reading order specified in Lua, with regular deducing calls") { const static std::tuple triple = std::make_tuple(10, 11, 12); sol::state lua; lua.set_function("f_string", []() { return "this is a string!"; }); sol::function f_string = lua["f_string"]; // Make sure there are no overload collisions / compiler errors for automatic string conversions std::string f_string_result = f_string(); REQUIRE(f_string_result == "this is a string!"); f_string_result = f_string(); REQUIRE(f_string_result == "this is a string!"); lua.set_function("f", [] { return std::make_tuple(10, 11, 12); }); lua.script("function g() return 10, 11, 12 end\nx,y,z = g()"); std::tuple tcpp = lua.get("f")(); std::tuple tlua = lua.get("g")(); std::tuple tluaget = lua.get("x", "y", "z"); INFO("cpp: " << std::get<0>(tcpp) << ',' << std::get<1>(tcpp) << ',' << std::get<2>(tcpp)); INFO("lua: " << std::get<0>(tlua) << ',' << std::get<1>(tlua) << ',' << std::get<2>(tlua)); INFO("lua xyz: " << lua.get("x") << ',' << lua.get("y") << ',' << lua.get("z")); REQUIRE(tcpp == triple); REQUIRE(tlua == triple); REQUIRE(tluaget == triple); } TEST_CASE("functions/optional values", "check if optionals can be passed in to be nil or otherwise") { struct thing { int v; }; sol::state lua; lua.script(R"( function f (a) return a end )"); sol::function lua_bark = lua["f"]; sol::optional testv = lua_bark(sol::optional(29)); sol::optional testn = lua_bark(sol::nullopt); REQUIRE((bool)testv); REQUIRE_FALSE((bool)testn); REQUIRE(testv.value() == 29); sol::optional v = lua_bark(sol::optional(thing{ 29 })); REQUIRE_NOTHROW([&] {sol::nil_t n = lua_bark(sol::nullopt); return n; }()); REQUIRE(v->v == 29); } TEST_CASE("functions/pair and tuple and proxy tests", "Check if sol::reference and sol::proxy can be passed to functions as arguments") { sol::state lua; lua.new_usertype("A", "bark", &A::bark); lua.script(R"( function f (num_value, a) return num_value * 2, a:bark() end function h (num_value, a, b) return num_value * 2, a:bark(), b * 3 end nested = { variables = { no = { problem = 10 } } } )"); lua.set_function("g", bark); sol::function cpp_bark = lua["g"]; sol::function lua_bark = lua["f"]; sol::function lua_bark2 = lua["h"]; sol::reference lua_variable_x = lua["nested"]["variables"]["no"]["problem"]; A cpp_variable_y; static const std::tuple abdesired(20, 1); static const std::pair cddesired = { 20, 1 }; static const std::tuple abcdesired(20, 1, 3); std::tuple ab = cpp_bark(lua_variable_x, cpp_variable_y); std::pair cd = lua_bark(lua["nested"]["variables"]["no"]["problem"], cpp_variable_y); REQUIRE(ab == abdesired); REQUIRE(cd == cddesired); ab = cpp_bark(std::make_pair(lua_variable_x, cpp_variable_y)); cd = static_cast>(lua_bark(std::make_pair(lua["nested"]["variables"]["no"]["problem"], cpp_variable_y))); REQUIRE(ab == abdesired); REQUIRE(cd == cddesired); std::tuple abc = lua_bark2(std::make_tuple(10, cpp_variable_y), sol::optional(1)); REQUIRE(abc == abcdesired); } TEST_CASE("functions/sol::function to std::function", "check if conversion to std::function works properly and calls with correct arguments") { sol::state lua; lua.open_libraries(sol::lib::base); lua.set_function("testFunc", test_free_func); lua.set_function("testFunc2", test_free_func2); lua.script( "testFunc(function() print(\"hello std::function\") end)" ); REQUIRE_NOTHROW(lua.script( "function m(a)\n" " print(\"hello std::function with arg \", a)\n" " return a\n" "end\n" "\n" "testFunc2(m, 1)" )); } TEST_CASE("functions/returning functions from C++", "check to see if returning a functor and getting a functor from lua is possible") { sol::state lua; lua.open_libraries(sol::lib::base); lua.set_function("makefn", makefn); lua.set_function("takefn", takefn); lua.script("afx = makefn()\n" "print(afx())\n" "takefn(afx)\n"); } TEST_CASE("functions/function_result and protected_function_result", "Function result should be the beefy return type for sol::function that allows for error checking and error handlers") { sol::state lua; lua.open_libraries(sol::lib::base, sol::lib::debug); static const char unhandlederrormessage[] = "true error message"; static const char handlederrormessage[] = "doodle"; static const std::string handlederrormessage_s = handlederrormessage; // Some function; just using a lambda to be cheap auto doomfx = []() { INFO("doomfx called"); throw std::runtime_error(unhandlederrormessage); }; auto luadoomfx = [&lua]() { INFO("luadoomfx called"); // Does not bypass error function, will call it luaL_error(lua.lua_state(), unhandlederrormessage); }; lua.set_function("doom", doomfx); lua.set_function("luadoom", luadoomfx); auto cpphandlerfx = [](std::string x) { INFO("c++ handler called with: " << x); return handlederrormessage; }; lua.set_function("cpphandler", cpphandlerfx); lua.script( std::string("function luahandler ( message )") + " print('lua handler called with: ' .. message)" + " return '" + handlederrormessage + "'" + "end" ); auto nontrampolinefx = [](lua_State*) -> int { throw "x"; }; lua_CFunction c_nontrampolinefx = nontrampolinefx; lua.set("nontrampoline", c_nontrampolinefx); lua.set_function("bark", []() -> int { return 100; }); sol::function luahandler = lua["luahandler"]; sol::function cpphandler = lua["cpphandler"]; sol::protected_function doom(lua["doom"], luahandler); sol::protected_function luadoom(lua["luadoom"]); sol::protected_function nontrampoline = lua["nontrampoline"]; sol::protected_function justfine = lua["bark"]; sol::protected_function justfinewithhandler = lua["bark"]; luadoom.error_handler = cpphandler; nontrampoline.error_handler = cpphandler; justfinewithhandler.error_handler = luahandler; bool present = true; { sol::protected_function_result result = doom(); REQUIRE_FALSE(result.valid()); sol::optional operr = result; sol::optional opvalue = result; present = (bool)operr; REQUIRE(present); present = (bool)opvalue; REQUIRE_FALSE(present); sol::error err = result; REQUIRE(err.what() == handlederrormessage_s); } { sol::protected_function_result result = luadoom(); REQUIRE_FALSE(result.valid()); sol::optional operr = result; sol::optional opvalue = result; present = (bool)operr; REQUIRE(present); present = (bool)opvalue; REQUIRE_FALSE(present); sol::error err = result; REQUIRE(err.what() == handlederrormessage_s); } { sol::protected_function_result result = nontrampoline(); REQUIRE_FALSE(result.valid()); sol::optional operr = result; sol::optional opvalue = result; present = (bool)operr; REQUIRE(present); present = (bool)opvalue; REQUIRE_FALSE(present); sol::error err = result; REQUIRE(err.what() == handlederrormessage_s); } { sol::protected_function_result result = justfine(); REQUIRE(result.valid()); sol::optional operr = result; sol::optional opvalue = result; present = (bool)operr; REQUIRE_FALSE(present); present = (bool)opvalue; REQUIRE(present); int value = result; REQUIRE(value == 100); } { sol::protected_function_result result = justfinewithhandler(); REQUIRE(result.valid()); sol::optional operr = result; sol::optional opvalue = result; present = (bool)operr; REQUIRE_FALSE(present); present = (bool)opvalue; REQUIRE(present); int value = result; REQUIRE(value == 100); } } TEST_CASE("functions/destructor tests", "Show that proper copies / destruction happens") { static int created = 0; static int destroyed = 0; static void* last_call = nullptr; static void* static_call = reinterpret_cast(0x01); typedef void(*fptr)(); struct x { x() { ++created; } x(const x&) { ++created; } x(x&&) { ++created; } x& operator=(const x&) { return *this; } x& operator=(x&&) { return *this; } void func() { last_call = static_cast(this); }; ~x() { ++destroyed; } }; struct y { y() { ++created; } y(const x&) { ++created; } y(x&&) { ++created; } y& operator=(const x&) { return *this; } y& operator=(x&&) { return *this; } static void func() { last_call = static_call; }; void operator()() { func(); } operator fptr () { return func; } ~y() { ++destroyed; } }; // stateful functors/member functions should always copy unless specified { created = 0; destroyed = 0; last_call = nullptr; { sol::state lua; x x1; lua.set_function("x1copy", &x::func, x1); lua.script("x1copy()"); REQUIRE(created == 2); REQUIRE(destroyed == 0); REQUIRE_FALSE(last_call == &x1); lua.set_function("x1ref", &x::func, std::ref(x1)); lua.script("x1ref()"); REQUIRE(created == 2); REQUIRE(destroyed == 0); REQUIRE(last_call == &x1); } REQUIRE(created == 2); REQUIRE(destroyed == 2); } // things convertible to a static function should _never_ be forced to make copies // therefore, pass through untouched { created = 0; destroyed = 0; last_call = nullptr; { sol::state lua; y y1; lua.set_function("y1copy", y1); lua.script("y1copy()"); REQUIRE(created == 1); REQUIRE(destroyed == 0); REQUIRE(last_call == static_call); last_call = nullptr; lua.set_function("y1ref", std::ref(y1)); lua.script("y1ref()"); REQUIRE(created == 1); REQUIRE(destroyed == 0); REQUIRE(last_call == static_call); } REQUIRE(created == 1); REQUIRE(destroyed == 1); } } TEST_CASE("functions/all kinds", "Register all kinds of functions, make sure they all compile and work") { sol::state lua; struct test_1 { int a = 0xA; virtual int bark() { return a; } int bark_mem() { return a; } static std::tuple x_bark(int num_value, test_1* a) { return std::tuple(num_value * 2, a->a); } }; struct test_2 { int a = 0xC; int bark() { return 20; } }; struct inner { const int z = 5653; }; struct nested { inner i; }; auto a = []() { return 500; }; auto b = [&]() { return 501; }; auto c = [&]() { return 502; }; auto d = []() { return 503; }; lua.new_usertype("test_1", "bark", sol::c_call ); lua.new_usertype("test_2", "bark", sol::c_call ); test_2 t2; lua.set_function("a", a); lua.set_function("b", b); lua.set_function("c", std::ref(c)); lua.set_function("d", std::ref(d)); lua.set_function("f", &test_1::bark); lua.set_function("g", test_1::x_bark); lua.set_function("h", sol::c_call); lua.set_function("i", &test_2::bark, test_2()); lua.set_function("j", &test_2::a, test_2()); lua.set_function("k", &test_2::a); lua.set_function("l", sol::c_call); lua.set_function("m", &test_2::a, &t2); lua.set_function("n", sol::c_call); lua.script(R"( o1 = test_1.new() o2 = test_2.new() )"); lua.script(R"( ob = o1:bark() A = a() B = b() C = c() D = d() F = f(o1) G0, G1 = g(2, o1) H = h(o1) I = i(o1) I = i(o1) )"); lua.script(R"( J0 = j() j(24) J1 = j() )"); lua.script(R"( K0 = k(o2) k(o2, 1024) K1 = k(o2) )"); lua.script(R"( L0 = l(o1) l(o1, 678) L1 = l(o1) )"); lua.script(R"( M0 = m() m(256) M1 = m() )"); lua.script(R"( N = n(1, 2, 3) )"); int ob, A, B, C, D, F, G0, G1, H, I, J0, J1, K0, K1, L0, L1, M0, M1, N; std::tie(ob, A, B, C, D, F, G0, G1, H, I, J0, J1, K0, K1, L0, L1, M0, M1, N) = lua.get( "ob", "A", "B", "C", "D", "F", "G0", "G1", "H", "I", "J0", "J1", "K0", "K1", "L0", "L1", "M0", "M1", "N" ); REQUIRE(ob == 0xA); REQUIRE(A == 500); REQUIRE(B == 501); REQUIRE(C == 502); REQUIRE(D == 503); REQUIRE(F == 0xA); REQUIRE(G0 == 4); REQUIRE(G1 == 0xA); REQUIRE(H == 0xA); REQUIRE(I == 20); REQUIRE(J0 == 0xC); REQUIRE(J1 == 24); REQUIRE(K0 == 0xC); REQUIRE(K1 == 1024); REQUIRE(L0 == 0xA); REQUIRE(L1 == 678); REQUIRE(M0 == 0xC); REQUIRE(M1 == 256); REQUIRE(N == 13); sol::tie(ob, A, B, C, D, F, G0, G1, H, I, J0, J1, K0, K1, L0, L1, M0, M1, N) = lua.get( "ob", "A", "B", "C", "D", "F", "G0", "G1", "H", "I", "J0", "J1", "K0", "K1", "L0", "L1", "M0", "M1", "N" ); REQUIRE(ob == 0xA); REQUIRE(A == 500); REQUIRE(B == 501); REQUIRE(C == 502); REQUIRE(D == 503); REQUIRE(F == 0xA); REQUIRE(G0 == 4); REQUIRE(G1 == 0xA); REQUIRE(H == 0xA); REQUIRE(I == 20); REQUIRE(J0 == 0xC); REQUIRE(J1 == 24); REQUIRE(K0 == 0xC); REQUIRE(K1 == 1024); REQUIRE(L0 == 0xA); REQUIRE(L1 == 678); REQUIRE(M0 == 0xC); REQUIRE(M1 == 256); REQUIRE(N == 13); // Work that compiler, WORK IT! lua.set("o", &test_1::bark); lua.set("p", test_1::x_bark); lua.set("q", sol::c_call); lua.set("r", &test_2::a); lua.set("s", sol::readonly(&test_2::a)); lua.set_function("t", sol::readonly(&test_2::a), test_2()); lua.set_function("u", &nested::i, nested()); lua.set("v", &nested::i); lua.set("nested", nested()); lua.set("inner", inner()); REQUIRE_THROWS(lua.script("s(o2, 2)")); REQUIRE_THROWS(lua.script("t(2)")); REQUIRE_THROWS(lua.script("u(inner)")); REQUIRE_THROWS(lua.script("v(nested, inner)")); } TEST_CASE("simple/call with parameters", "Lua function is called with a few parameters from C++") { sol::state lua; REQUIRE_NOTHROW(lua.script("function my_add(i, j, k) return i + j + k end")); auto f = lua.get("my_add"); REQUIRE_NOTHROW(lua.script("function my_nothing(i, j, k) end")); auto fvoid = lua.get("my_nothing"); int a; REQUIRE_NOTHROW(fvoid(1, 2, 3)); REQUIRE_NOTHROW(a = f.call(1, 2, 3)); REQUIRE(a == 6); REQUIRE_THROWS(a = f(1, 2, "arf")); } TEST_CASE("simple/call c++ function", "C++ function is called from lua") { sol::state lua; lua.set_function("plop_xyz", sep::plop_xyz); lua.script("x = plop_xyz(2, 6, 'hello')"); REQUIRE(lua.get("x") == 11); } TEST_CASE("simple/call lambda", "A C++ lambda is exposed to lua and called") { sol::state lua; int a = 0; lua.set_function("foo", [&a] { a = 1; }); lua.script("foo()"); REQUIRE(a == 1); } TEST_CASE("advanced/get and call", "Checks for lambdas returning values after a get operation") { const static std::string lol = "lol", str = "str"; const static std::tuple heh_tuple = std::make_tuple(1, 6.28f, 3.14, std::string("heh")); sol::state lua; REQUIRE_NOTHROW(lua.set_function("a", [] { return 42; })); REQUIRE(lua.get("a").call() == 42); REQUIRE_NOTHROW(lua.set_function("b", [] { return 42u; })); REQUIRE(lua.get("b").call() == 42u); REQUIRE_NOTHROW(lua.set_function("c", [] { return 3.14; })); REQUIRE(lua.get("c").call() == 3.14); REQUIRE_NOTHROW(lua.set_function("d", [] { return 6.28f; })); REQUIRE(lua.get("d").call() == 6.28f); REQUIRE_NOTHROW(lua.set_function("e", [] { return "lol"; })); REQUIRE(lua.get("e").call() == lol); REQUIRE_NOTHROW(lua.set_function("f", [] { return true; })); REQUIRE(lua.get("f").call()); REQUIRE_NOTHROW(lua.set_function("g", [] { return std::string("str"); })); REQUIRE(lua.get("g").call() == str); REQUIRE_NOTHROW(lua.set_function("h", [] {})); REQUIRE_NOTHROW(lua.get("h").call()); REQUIRE_NOTHROW(lua.set_function("i", [] { return sol::nil; })); REQUIRE(lua.get("i").call() == sol::nil); REQUIRE_NOTHROW(lua.set_function("j", [] { return std::make_tuple(1, 6.28f, 3.14, std::string("heh")); })); REQUIRE((lua.get("j").call() == heh_tuple)); } TEST_CASE("advanced/operator[] call", "Checks for lambdas returning values using operator[]") { const static std::string lol = "lol", str = "str"; const static std::tuple heh_tuple = std::make_tuple(1, 6.28f, 3.14, std::string("heh")); sol::state lua; REQUIRE_NOTHROW(lua.set_function("a", [] { return 42; })); REQUIRE(lua["a"].call() == 42); REQUIRE_NOTHROW(lua.set_function("b", [] { return 42u; })); REQUIRE(lua["b"].call() == 42u); REQUIRE_NOTHROW(lua.set_function("c", [] { return 3.14; })); REQUIRE(lua["c"].call() == 3.14); REQUIRE_NOTHROW(lua.set_function("d", [] { return 6.28f; })); REQUIRE(lua["d"].call() == 6.28f); REQUIRE_NOTHROW(lua.set_function("e", [] { return "lol"; })); REQUIRE(lua["e"].call() == lol); REQUIRE_NOTHROW(lua.set_function("f", [] { return true; })); REQUIRE(lua["f"].call()); REQUIRE_NOTHROW(lua.set_function("g", [] { return std::string("str"); })); REQUIRE(lua["g"].call() == str); REQUIRE_NOTHROW(lua.set_function("h", [] {})); REQUIRE_NOTHROW(lua["h"].call()); REQUIRE_NOTHROW(lua.set_function("i", [] { return sol::nil; })); REQUIRE(lua["i"].call() == sol::nil); REQUIRE_NOTHROW(lua.set_function("j", [] { return std::make_tuple(1, 6.28f, 3.14, std::string("heh")); })); REQUIRE((lua["j"].call() == heh_tuple)); } TEST_CASE("advanced/call lambdas", "A C++ lambda is exposed to lua and called") { sol::state lua; int x = 0; lua.set_function("set_x", [&](int new_x) { x = new_x; return 0; }); lua.script("set_x(9)"); REQUIRE(x == 9); } TEST_CASE("advanced/call referenced obj", "A C++ object is passed by pointer/reference_wrapper to lua and invoked") { sol::state lua; int x = 0; auto objx = [&](int new_x) { x = new_x; return 0; }; lua.set_function("set_x", std::ref(objx)); int y = 0; auto objy = [&](int new_y) { y = new_y; return std::tuple(0, 0); }; lua.set_function("set_y", &decltype(objy)::operator(), std::ref(objy)); lua.script("set_x(9)"); lua.script("set_y(9)"); REQUIRE(x == 9); REQUIRE(y == 9); } TEST_CASE("functions/tie", "make sure advanced syntax with 'tie' works") { sol::state lua; lua.script(R"(function f () return 1, 2, 3 end)"); sol::function f = lua["f"]; int a, b, c; sol::tie(a, b, c) = f(); REQUIRE(a == 1); REQUIRE(b == 2); REQUIRE(c == 3); } TEST_CASE("functions/overloading", "Check if overloading works properly for regular set function syntax") { sol::state lua; lua.open_libraries(sol::lib::base); lua.set_function("func_1", func_1); lua.set_function("func", sol::overload(func_2, func_3, func_1, func_1s)); const std::string string_bark = "string: bark"; REQUIRE_NOTHROW(lua.script( "a = func(1)\n" "b = func('bark')\n" "c = func(1,2)\n" "func(1,2,3)\n" )); REQUIRE((lua["a"] == 1)); REQUIRE((lua["b"] == string_bark)); REQUIRE((lua["c"] == 2)); REQUIRE_THROWS(lua.script("func(1,2,'meow')")); } TEST_CASE("overloading/c_call", "Make sure that overloading works with c_call functionality") { sol::state lua; lua.set("f", sol::c_call, sol::wrap, sol::wrap>); lua.set("g", sol::c_call>); lua.set("h", sol::c_call); lua.set("obj", fer()); lua.script("r1 = f(1)"); lua.script("r2 = f(1, 2)"); lua.script("r3 = f(obj, 1, 2)"); lua.script("r4 = g(1)"); lua.script("r5 = h(1, 2)"); int r1 = lua["r1"]; int r2 = lua["r2"]; double r3 = lua["r3"]; int r4 = lua["r4"]; int r5 = lua["r5"]; REQUIRE(r1 == 32); REQUIRE(r2 == 1); REQUIRE(r3 == 2.5); REQUIRE(r4 == 32); REQUIRE(r5 == 1); } TEST_CASE("functions/stack atomic", "make sure functions don't impede on the stack") { //setup sol/lua sol::state lua; lua.open_libraries(sol::lib::base, sol::lib::string); lua.script("function ErrorHandler(msg) print('Lua created error msg : ' .. msg) return msg end"); lua.script("function stringtest(a) if a == nil then error('fuck') end print('Lua recieved content : ' .. a) return a end"); // test normal function { sol::stack_guard normalsg(lua); std::string str = lua["stringtest"]("normal test"); INFO("Back in C++, direct call result is : " << str); } //test protected_function sol::protected_function Stringtest(lua["stringtest"]); Stringtest.error_handler = lua["ErrorHandler"]; sol::stack_guard sg(lua); { sol::protected_function_result stringresult = Stringtest("protected test"); REQUIRE(stringresult.valid()); std::string s = stringresult; INFO("Back in C++, protected result is : " << s); } REQUIRE(sg.check_stack()); //test optional { sol::stack_guard opsg(lua); sol::optional opt_result = Stringtest("optional test"); REQUIRE(opsg.check_stack()); if (opt_result) { std::string s = opt_result.value(); INFO("Back in C++, opt_result is : " << s); } else { INFO("opt_result failed"); } } REQUIRE(sg.check_stack()); { sol::protected_function_result errresult = Stringtest(sol::nil); REQUIRE_FALSE(errresult.valid()); sol::error err = errresult; std::string msg = err.what(); INFO("error :" << msg); } REQUIRE(sg.check_stack()); } TEST_CASE("functions/same type closures", "make sure destructions are per-object, not per-type, by destroying one type multiple times") { static std::set last_my_closures; static bool checking_closures = false; static bool check_failed = false; struct my_closure { int& n; my_closure(int& n) : n(n) {} ~my_closure() noexcept(false) { if (!checking_closures) return; void* addr = static_cast(this); auto f = last_my_closures.find(addr); if (f != last_my_closures.cend()) { check_failed = true; } last_my_closures.insert(f, addr); } int operator() () { ++n; return n; } }; int n = 250; my_closure a(n); my_closure b(n); { sol::state lua; lua.set_function("f", a); lua.set_function("g", b); checking_closures = true; } REQUIRE_FALSE(check_failed); REQUIRE(last_my_closures.size() == 2); } TEST_CASE("functions/stack multi-return", "Make sure the stack is protected after multi-returns") { sol::state lua; lua.script("function f () return 1, 2, 3, 4, 5 end"); { sol::stack_guard sg(lua); sol::stack::push(lua, double(256.78)); { int a, b, c, d, e; sol::stack_guard sg2(lua); sol::function f = lua["f"]; sol::tie(a, b, c, d, e) = f(); REQUIRE(a == 1); REQUIRE(b == 2); REQUIRE(c == 3); REQUIRE(d == 4); REQUIRE(e == 5); } double f = sol::stack::pop(lua); REQUIRE(f == 256.78); } } TEST_CASE("functions/protected stack multi-return", "Make sure the stack is protected after multi-returns") { sol::state lua; lua.script("function f () return 1, 2, 3, 4, 5 end"); { sol::stack_guard sg(lua); sol::stack::push(lua, double(256.78)); { int a, b, c, d, e; sol::stack_guard sg2(lua); sol::protected_function pf = lua["f"]; sol::tie(a, b, c, d, e) = pf(); REQUIRE(a == 1); REQUIRE(b == 2); REQUIRE(c == 3); REQUIRE(d == 4); REQUIRE(e == 5); } double f = sol::stack::pop(lua); REQUIRE(f == 256.78); } } TEST_CASE("functions/function_result as arguments", "ensure that function_result can be pushed as its results and not a userdata") { sol::state lua; lua.open_libraries(); lua.script("function f () return 1, 2, 3, 4, 5 end"); lua.script("function g (a, b, c, d, e) assert(a == 1) assert(b == 2) assert(c == 3) assert(d == 4) assert(e == 5) end"); { sol::stack_guard sg(lua); sol::stack::push(lua, double(256.78)); { int a, b, c, d, e; sol::stack_guard sg2(lua); sol::function pf = lua["f"]; sol::tie(a, b, c, d, e) = pf(); REQUIRE(a == 1); REQUIRE(b == 2); REQUIRE(c == 3); REQUIRE(d == 4); REQUIRE(e == 5); REQUIRE_NOTHROW([&]() { lua["g"](pf()); }()); } double f = sol::stack::pop(lua); REQUIRE(f == 256.78); } } TEST_CASE("functions/protected_function_result as arguments", "ensure that protected_function_result can be pushed as its results and not a userdata") { sol::state lua; lua.open_libraries(); lua.script("function f () return 1, 2, 3, 4, 5 end"); lua.script("function g (a, b, c, d, e) assert(a == 1) assert(b == 2) assert(c == 3) assert(d == 4) assert(e == 5) end"); { sol::stack_guard sg(lua); sol::stack::push(lua, double(256.78)); { int a, b, c, d, e; sol::stack_guard sg2(lua); sol::protected_function pf = lua["f"]; sol::tie(a, b, c, d, e) = pf(); REQUIRE(a == 1); REQUIRE(b == 2); REQUIRE(c == 3); REQUIRE(d == 4); REQUIRE(e == 5); REQUIRE_NOTHROW([&]() { lua["g"](pf()); }()); } double f = sol::stack::pop(lua); REQUIRE(f == 256.78); } } TEST_CASE("functions/overloaded variadic", "make sure variadics work to some degree with overloading") { sol::state lua; lua.open_libraries(); sol::table ssl = lua.create_named_table("ssl"); ssl.set_function("test", sol::overload(&va_func, &va_func)); lua.script("a = ssl.test(1, 2, 3)"); lua.script("b = ssl.test(1, 2)"); lua.script("c = ssl.test(2.2)"); int a = lua["a"]; int b = lua["b"]; double c = lua["c"]; REQUIRE(a == 6); REQUIRE(b == 3); REQUIRE(c == 2.2); } TEST_CASE("functions/sectioning variadic", "make sure variadics can bite off chunks of data") { sol::state lua; lua.open_libraries(sol::lib::base); lua.set_function("f", [](sol::variadic_args va) { int r = 0; sol::variadic_args shifted_va(va.lua_state(), 3); for (auto v : shifted_va) { int value = v; r += value; } return r; }); lua.script("x = f(1, 2, 3, 4)"); lua.script("x2 = f(8, 200, 3, 4)"); lua.script("x3 = f(1, 2, 3, 4, 5, 6)"); lua.script("print(x) assert(x == 7)"); lua.script("print(x2) assert(x2 == 7)"); lua.script("print(x3) assert(x3 == 18)"); } TEST_CASE("functions/set_function already wrapped", "setting a function returned from Lua code that is already wrapped into a sol::function or similar") { SECTION("test different types") { sol::state lua; lua.open_libraries(sol::lib::base); sol::function fn = lua.script("return function() return 5 end"); sol::protected_function pfn = fn; std::function sfn = fn; lua.set_function("test", fn); lua.set_function("test2", pfn); lua.set_function("test3", sfn); REQUIRE_NOTHROW(lua.script("assert(type(test) == 'function')")); REQUIRE_NOTHROW(lua.script("assert(test() ~= nil)")); REQUIRE_NOTHROW(lua.script("assert(test() == 5)")); REQUIRE_NOTHROW(lua.script("assert(type(test2) == 'function')")); REQUIRE_NOTHROW(lua.script("assert(test2() ~= nil)")); REQUIRE_NOTHROW(lua.script("assert(test2() == 5)")); REQUIRE_NOTHROW(lua.script("assert(type(test3) == 'function')")); REQUIRE_NOTHROW(lua.script("assert(test3() ~= nil)")); REQUIRE_NOTHROW(lua.script("assert(test3() == 5)")); } SECTION("getting the value from C++") { sol::state lua; lua.open_libraries(sol::lib::base); sol::function fn = lua.script("return function() return 5 end"); int result = fn(); REQUIRE(result == 5); } SECTION("setting the function directly") { sol::state lua; lua.open_libraries(sol::lib::base); sol::function fn = lua.script("return function() return 5 end"); lua.set_function("test", fn); REQUIRE_NOTHROW(lua.script("assert(type(test) == 'function')")); REQUIRE_NOTHROW(lua.script("assert(test() ~= nil)")); REQUIRE_NOTHROW(lua.script("assert(test() == 5)")); } SECTION("does the function actually get executed?") { sol::state lua; lua.open_libraries(sol::lib::base); sol::function fn2 = lua.script("return function() print('this was executed') end"); lua.set_function("test", fn2); REQUIRE_NOTHROW(lua.script("assert(type(test) == 'function')")); REQUIRE_NOTHROW(lua.script("test()")); } SECTION("setting the function indirectly, with the return value cast explicitly") { sol::state lua; lua.open_libraries(sol::lib::base); sol::function fn = lua.script("return function() return 5 end"); lua.set_function("test", [&fn]() { return fn.call(); }); REQUIRE_NOTHROW(lua.script("assert(type(test) == 'function')")); REQUIRE_NOTHROW(lua.script("assert(test() ~= nil)")); REQUIRE_NOTHROW(lua.script("assert(test() == 5)")); } } TEST_CASE("functions/pointer nullptr + nil", "ensure specific semantics for handling pointer-nils passed through sol") { struct nil_test { static void f(nil_test* p) { REQUIRE(p == nullptr); } static void g(std::unique_ptr& p) { REQUIRE(p == nullptr); } static void h(std::shared_ptr& p) { REQUIRE(p == nullptr); } }; std::shared_ptr sptr; std::unique_ptr uptr; std::unique_ptr ruptr; nil_test* rptr = ruptr.get(); nil_test* vptr = nullptr; SECTION("ptr") { sol::state lua; lua["v1"] = sptr; lua["v2"] = std::unique_ptr(); lua["v3"] = rptr; lua["v4"] = vptr; REQUIRE_NOTHROW([&]() { nil_test* v1 = lua["v1"]; nil_test* v2 = lua["v2"]; nil_test* v3 = lua["v3"]; nil_test* v4 = lua["v4"]; REQUIRE(v1 == sptr.get()); REQUIRE(v1 == nullptr); REQUIRE(v2 == uptr.get()); REQUIRE(v2 == nullptr); REQUIRE(v3 == rptr); REQUIRE(v3 == nullptr); REQUIRE(v4 == vptr); REQUIRE(v4 == nullptr); }()); } SECTION("ptr") { sol::state lua; lua.open_libraries(); lua["v1"] = sptr; lua["v2"] = std::unique_ptr(); lua["v3"] = rptr; lua["v4"] = vptr; lua["f"] = &nil_test::f; lua["g"] = &nil_test::g; lua["h"] = &nil_test::h; REQUIRE_NOTHROW([&]() { lua.script("f(v1)"); lua.script("f(v2)"); lua.script("f(v3)"); lua.script("f(v4)"); lua.script("assert(v1 == nil)"); lua.script("assert(v2 == nil)"); lua.script("assert(v3 == nil)"); lua.script("assert(v4 == nil)"); }()); } SECTION("throw unique argument") { sol::state lua; lua["v2"] = std::unique_ptr(); lua["g"] = &nil_test::g; REQUIRE_THROWS([&]() { lua.script("g(v2)"); }()); } SECTION("throw shared argument") { sol::state lua; lua["v1"] = sptr; lua["h"] = &nil_test::h; REQUIRE_THROWS([&]() { lua.script("h(v1)"); }()); } SECTION("throw ref") { REQUIRE_THROWS([&]() { sol::state lua; lua["v1"] = sptr; nil_test& v1 = lua["v1"]; (void)(&v1 == sptr.get()); }()); REQUIRE_THROWS([&]() { sol::state lua; lua["v2"] = std::unique_ptr(); nil_test& v2 = lua["v2"]; (void)(&v2 == uptr.get()); }()); REQUIRE_THROWS([&]() { sol::state lua; lua["v3"] = rptr; nil_test& v3 = lua["v3"]; (void)(&v3 == rptr); }()); REQUIRE_THROWS([&]() { sol::state lua; lua["v4"] = vptr; nil_test& v4 = lua["v4"]; (void)(&v4 == vptr); }()); } SECTION("throw unique") { REQUIRE_THROWS([&]() { sol::state lua; lua["v1"] = sptr; std::unique_ptr& v1 = lua["v1"]; (void)(v1.get() == sptr.get()); }()); REQUIRE_THROWS([&]() { sol::state lua; lua["v2"] = std::unique_ptr(); std::unique_ptr& v2 = lua["v2"]; (void)(v2.get() == uptr.get()); }()); REQUIRE_THROWS([&]() { sol::state lua; lua["v3"] = rptr; std::unique_ptr& v3 = lua["v3"]; (void)(v3.get() == rptr); }()); REQUIRE_THROWS([&]() { sol::state lua; lua["v4"] = vptr; std::unique_ptr& v4 = lua["v4"]; (void)(v4.get() == vptr); }()); } SECTION("throw shared") { REQUIRE_THROWS([&]() { sol::state lua; lua["v1"] = sptr; std::shared_ptr& v1 = lua["v1"]; (void)(v1.get() == sptr.get()); }()); REQUIRE_THROWS([&]() { sol::state lua; lua["v2"] = std::unique_ptr(); std::shared_ptr& v2 = lua["v2"]; (void)(v2.get() == uptr.get()); }()); REQUIRE_THROWS([&]() { sol::state lua; lua["v3"] = rptr; std::shared_ptr& v3 = lua["v3"]; (void)(v3.get() == rptr); }()); REQUIRE_THROWS([&]() { sol::state lua; lua["v4"] = vptr; std::shared_ptr& v4 = lua["v4"]; (void)(v4.get() == vptr); }()); } } TEST_CASE("functions/unique_usertype overloading", "make sure overloading can work with ptr vs. specifically asking for a unique_usertype") { struct test { int special_value = 17; test() : special_value(17) {} test(int special_value) : special_value(special_value) {} }; auto print_up_test = [](std::unique_ptr& x) { REQUIRE(x->special_value == 21); }; auto print_up_2_test = [](int, std::unique_ptr& x) { REQUIRE(x->special_value == 21); }; auto print_sp_test = [](std::shared_ptr& x) { REQUIRE(x->special_value == 44); }; auto print_ptr_test = [](test* x) { REQUIRE(x->special_value == 17); }; auto print_ref_test = [](test& x) { bool is_any = x.special_value == 17 || x.special_value == 21 || x.special_value == 44; REQUIRE(is_any); }; using f_t = void(test&); f_t* fptr = print_ref_test; std::unique_ptr ut = std::make_unique(17); SECTION("working") { sol::state lua; lua.set_function("f", print_up_test); lua.set_function("g", sol::overload( std::move(print_sp_test), print_up_test, std::ref(print_ptr_test) )); lua.set_function("h", std::ref(fptr)); lua.set_function("i", std::move(print_up_2_test)); lua["v1"] = std::make_unique(21); lua["v2"] = std::make_shared(44); lua["v3"] = test(17); lua["v4"] = ut.get(); REQUIRE_NOTHROW([&]() { lua.script("f(v1)"); lua.script("g(v1)"); lua.script("g(v2)"); lua.script("g(v3)"); lua.script("g(v4)"); lua.script("h(v1)"); lua.script("h(v2)"); lua.script("h(v3)"); lua.script("h(v4)"); lua.script("i(20, v1)"); }()); }; // LuaJIT segfaults hard on some Linux machines // and it breaks all the tests... SECTION("throws-value") { sol::state lua; lua.set_function("f", print_up_test); lua["v3"] = test(17); REQUIRE_THROWS([&]() { lua.script("f(v3)"); }()); }; SECTION("throws-shared_ptr") { sol::state lua; lua.set_function("f", print_up_test); lua["v2"] = std::make_shared(44); REQUIRE_THROWS([&]() { lua.script("f(v2)"); }()); }; SECTION("throws-ptr") { sol::state lua; lua.set_function("f", print_up_test); lua["v4"] = ut.get(); REQUIRE_THROWS([&]() { lua.script("f(v4)"); }()); }; } TEST_CASE("functions/noexcept", "allow noexcept functions to be serialized properly into Lua using sol2") { struct T { static int noexcept_function() noexcept { return 0x61; } int noexcept_method() noexcept { return 0x62; } } t; lua_CFunction ccall = sol::c_call; sol::state lua; lua.set_function("f", &T::noexcept_function); lua.set_function("g", &T::noexcept_method); lua.set_function("h", &T::noexcept_method, T()); lua.set_function("i", &T::noexcept_method, std::ref(t)); lua.set_function("j", &T::noexcept_method, &t); lua.set_function("k", &T::noexcept_method, t); lua.set_function("l", &raw_noexcept_function); lua.set_function("m", ccall); lua["t"] = T(); lua.script("v1 = f()"); lua.script("v2 = g(t)"); lua.script("v3 = h()"); lua.script("v4 = i()"); lua.script("v5 = j()"); lua.script("v6 = k()"); lua.script("v7 = l()"); lua.script("v8 = m()"); int v1 = lua["v1"]; int v2 = lua["v2"]; int v3 = lua["v3"]; int v4 = lua["v4"]; int v5 = lua["v5"]; int v6 = lua["v6"]; int v7 = lua["v7"]; int v8 = lua["v8"]; REQUIRE(v1 == 0x61); REQUIRE(v2 == 0x62); REQUIRE(v3 == 0x62); REQUIRE(v4 == 0x62); REQUIRE(v5 == 0x62); REQUIRE(v6 == 0x62); REQUIRE(v7 == 0x63); REQUIRE(v8 == 0x63); }