sol2/test_functions.cpp
ThePhD eb1560d12a add as_container documentation
improve exception documentation
improve state_view default handlers
add SAFE_PROPAGATION defines for compiling C++ as Lua
add examples for automatic operator registrations and as_container
fix tutorial code
change tests to not throw unless absolutely necessary
provide synchronization for file writing in tests
provide thread safety around thread tests for REQUIRE
add ostream automatic support
change 5.1 compat to only kick in luaL_loadbufferx and luaL_loadfilex when LuaJIT is version 2.0.1 and lower
2017-08-11 03:24:17 -04:00

1501 lines
40 KiB
C++

#define SOL_CHECK_ARGUMENTS
#include <catch.hpp>
#include <sol.hpp>
#include <iostream>
#include "test_stack_guard.hpp"
template <typename T>
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<int()> makefn() {
auto fx = []() -> int {
return 0x1456789;
};
return fx;
}
void takefn(std::function<int()> purr) {
if (purr() != 0x1456789)
throw 0;
}
struct A {
int a = 0xA; int bark() { return 1; }
};
std::tuple<int, int> bark(int num_value, A* a) {
return std::tuple<int, int>(num_value * 2, a->bark());
}
void test_free_func(std::function<void()> f) {
f();
}
void test_free_func2(std::function<int(int)> 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<std::string, int> 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<int>("overloaded", overloaded);
REQUIRE_NOTHROW(lua.script("print(overloaded(1))"));
lua.set_function<int, int>("overloaded", overloaded);
REQUIRE_NOTHROW(lua.script("print(overloaded(1, 2))"));
lua.set_function<int, int, int>("overloaded", overloaded);
REQUIRE_NOTHROW(lua.script("print(overloaded(1, 2, 3))"));
*/
lua.set_function("overloaded", sol::resolve<int(int)>(overloaded));
REQUIRE_NOTHROW(lua.script("print(overloaded(1))"));
lua.set_function("overloaded", sol::resolve<int(int, int)>(overloaded));
REQUIRE_NOTHROW(lua.script("print(overloaded(1, 2))"));
lua.set_function("overloaded", sol::resolve<int(int, int, int)>(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<int, int, int> triple = std::make_tuple(10, 11, 12);
const static std::tuple<int, float> 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<sol::function>("f").call<int, int, int>();
auto tlua = lua.get<sol::function>("g").call<int, int, int>();
auto tcpp2 = lua.get<sol::function>("h").call<int, float>();
auto tluaget = lua.get<int, int, int>("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<int, int, int> 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<int, int, int> tcpp = lua.get<sol::function>("f")();
std::tuple<int, int, int> tlua = lua.get<sol::function>("g")();
std::tuple<int, int, int> tluaget = lua.get<int, int, int>("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<int>("x") << ',' << lua.get<int>("y") << ',' << lua.get<int>("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<int> testv = lua_bark(sol::optional<int>(29));
sol::optional<int> testn = lua_bark(sol::nullopt);
REQUIRE((bool)testv);
REQUIRE_FALSE((bool)testn);
REQUIRE(testv.value() == 29);
sol::optional<thing> v = lua_bark(sol::optional<thing>(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>("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<int, int> abdesired(20, 1);
static const std::pair<int, int> cddesired = { 20, 1 };
static const std::tuple<int, int, int> abcdesired(20, 1, 3);
std::tuple<int, int> ab = cpp_bark(lua_variable_x, cpp_variable_y);
std::pair<int, int> 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<std::pair<int, int>>(lua_bark(std::make_pair(lua["nested"]["variables"]["no"]["problem"], cpp_variable_y)));
REQUIRE(ab == abdesired);
REQUIRE(cd == cddesired);
std::tuple<int, int, int> abc = lua_bark2(std::make_tuple(10, cpp_variable_y), sol::optional<int>(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<sol::error> operr = result;
sol::optional<int> 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<sol::error> operr = result;
sol::optional<int> 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<sol::error> operr = result;
sol::optional<int> 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<sol::error> operr = result;
sol::optional<int> 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<sol::error> operr = result;
sol::optional<int> 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<void*>(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<void*>(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<int, int> x_bark(int num_value, test_1* a) {
return std::tuple<int, int>(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>("test_1",
"bark", sol::c_call<decltype(&test_1::bark_mem), &test_1::bark_mem>
);
lua.new_usertype<test_2>("test_2",
"bark", sol::c_call<decltype(&test_2::bark), &test_2::bark>
);
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<decltype(&test_1::bark_mem), &test_1::bark_mem>);
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<decltype(&test_1::a), &test_1::a>);
lua.set_function("m", &test_2::a, &t2);
lua.set_function("n", sol::c_call<decltype(&non_overloaded), &non_overloaded>);
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<int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int>(
"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<int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int>(
"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<decltype(&test_1::bark_mem), &test_1::bark_mem>);
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());
{
auto result = lua.safe_script("s(o2, 2)", sol::script_pass_on_error);
REQUIRE_FALSE(result.valid());
}
{
auto result = lua.safe_script("t(2)", sol::script_pass_on_error);
REQUIRE_FALSE(result.valid());
}
{
auto result = lua.safe_script("u(inner)", sol::script_pass_on_error);
REQUIRE_FALSE(result.valid());
}
{
auto result = lua.safe_script("v(nested, inner)", sol::script_pass_on_error);
REQUIRE_FALSE(result.valid());
}
}
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<sol::function>("my_add");
REQUIRE_NOTHROW(lua.script("function my_nothing(i, j, k) end"));
auto fvoid = lua.get<sol::function>("my_nothing");
REQUIRE_NOTHROW([&]() {
fvoid(1, 2, 3);
}());
REQUIRE_NOTHROW([&]() {
int a = f.call<int>(1, 2, 3);
REQUIRE(a == 6);
}());
sol::protected_function pf = f;
REQUIRE_NOTHROW([&]() {
sol::protected_function_result pfr = pf(1, 2, "arf");
REQUIRE_FALSE(pfr.valid());
}());
}
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<int>("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<int, float, double, std::string> 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<sol::function>("a").call<int>() == 42);
REQUIRE_NOTHROW(lua.set_function("b", [] { return 42u; }));
REQUIRE(lua.get<sol::function>("b").call<unsigned int>() == 42u);
REQUIRE_NOTHROW(lua.set_function("c", [] { return 3.14; }));
REQUIRE(lua.get<sol::function>("c").call<double>() == 3.14);
REQUIRE_NOTHROW(lua.set_function("d", [] { return 6.28f; }));
REQUIRE(lua.get<sol::function>("d").call<float>() == 6.28f);
REQUIRE_NOTHROW(lua.set_function("e", [] { return "lol"; }));
REQUIRE(lua.get<sol::function>("e").call<std::string>() == lol);
REQUIRE_NOTHROW(lua.set_function("f", [] { return true; }));
REQUIRE(lua.get<sol::function>("f").call<bool>());
REQUIRE_NOTHROW(lua.set_function("g", [] { return std::string("str"); }));
REQUIRE(lua.get<sol::function>("g").call<std::string>() == str);
REQUIRE_NOTHROW(lua.set_function("h", [] {}));
REQUIRE_NOTHROW(lua.get<sol::function>("h").call());
REQUIRE_NOTHROW(lua.set_function("i", [] { return sol::nil; }));
REQUIRE(lua.get<sol::function>("i").call<sol::nil_t>() == sol::nil);
REQUIRE_NOTHROW(lua.set_function("j", [] { return std::make_tuple(1, 6.28f, 3.14, std::string("heh")); }));
REQUIRE((lua.get<sol::function>("j").call<int, float, double, std::string>() == 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<int, float, double, std::string> 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<int>() == 42);
REQUIRE_NOTHROW(lua.set_function("b", [] { return 42u; }));
REQUIRE(lua["b"].call<unsigned int>() == 42u);
REQUIRE_NOTHROW(lua.set_function("c", [] { return 3.14; }));
REQUIRE(lua["c"].call<double>() == 3.14);
REQUIRE_NOTHROW(lua.set_function("d", [] { return 6.28f; }));
REQUIRE(lua["d"].call<float>() == 6.28f);
REQUIRE_NOTHROW(lua.set_function("e", [] { return "lol"; }));
REQUIRE(lua["e"].call<std::string>() == lol);
REQUIRE_NOTHROW(lua.set_function("f", [] { return true; }));
REQUIRE(lua["f"].call<bool>());
REQUIRE_NOTHROW(lua.set_function("g", [] { return std::string("str"); }));
REQUIRE(lua["g"].call<std::string>() == 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_t>() == sol::nil);
REQUIRE_NOTHROW(lua.set_function("j", [] { return std::make_tuple(1, 6.28f, 3.14, std::string("heh")); }));
REQUIRE((lua["j"].call<int, float, double, std::string>() == 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<int, int>(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));
auto result = lua.safe_script("func(1,2,'meow')", sol::script_pass_on_error);
REQUIRE_FALSE(result.valid());
}
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<decltype(&f1), &f1>, sol::wrap<decltype(&f2), &f2>, sol::wrap<decltype(&fer::f3), &fer::f3>>);
lua.set("g", sol::c_call<sol::wrap<decltype(&f1), &f1>>);
lua.set("h", sol::c_call<decltype(&f2), &f2>);
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<std::string> 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<void*> 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<void*>(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<double>(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<double>(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<double>(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<double>(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<int>, &va_func<double>));
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<int()> 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<int>(); });
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<nil_test>& p) {
REQUIRE(p == nullptr);
}
static void h(std::shared_ptr<nil_test>& p) {
REQUIRE(p == nullptr);
}
};
std::shared_ptr<nil_test> sptr;
std::unique_ptr<nil_test> uptr;
std::unique_ptr<nil_test> ruptr;
nil_test* rptr = ruptr.get();
nil_test* vptr = nullptr;
SECTION("ptr") {
sol::state lua;
lua["v1"] = sptr;
lua["v2"] = std::unique_ptr<nil_test>();
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<nil_test>();
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<nil_test>();
lua["g"] = &nil_test::g;
auto result = lua.safe_script("g(v2)", sol::script_pass_on_error);
REQUIRE_FALSE(result.valid());
}
SECTION("throw shared argument") {
sol::state lua;
lua["v1"] = sptr;
lua["h"] = &nil_test::h;
auto result = lua.safe_script("h(v1)", sol::script_pass_on_error);
REQUIRE_FALSE(result.valid());
}
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>();
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<nil_test>& v1 = lua["v1"];
(void)(v1.get() == sptr.get());
}());
REQUIRE_THROWS([&]() {
sol::state lua;
lua["v2"] = std::unique_ptr<nil_test>();
std::unique_ptr<nil_test>& v2 = lua["v2"];
(void)(v2.get() == uptr.get());
}());
REQUIRE_THROWS([&]() {
sol::state lua;
lua["v3"] = rptr;
std::unique_ptr<nil_test>& v3 = lua["v3"];
(void)(v3.get() == rptr);
}());
REQUIRE_THROWS([&]() {
sol::state lua;
lua["v4"] = vptr;
std::unique_ptr<nil_test>& v4 = lua["v4"];
(void)(v4.get() == vptr);
}());
}
SECTION("throw shared") {
REQUIRE_THROWS([&]() {
sol::state lua;
lua["v1"] = sptr;
std::shared_ptr<nil_test>& v1 = lua["v1"];
(void)(v1.get() == sptr.get());
}());
REQUIRE_THROWS([&]() {
sol::state lua;
lua["v2"] = std::unique_ptr<nil_test>();
std::shared_ptr<nil_test>& v2 = lua["v2"];
(void)(v2.get() == uptr.get());
}());
REQUIRE_THROWS([&]() {
sol::state lua;
lua["v3"] = rptr;
std::shared_ptr<nil_test>& v3 = lua["v3"];
(void)(v3.get() == rptr);
}());
REQUIRE_THROWS([&]() {
sol::state lua;
lua["v4"] = vptr;
std::shared_ptr<nil_test>& 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<test>& x) {
REQUIRE(x->special_value == 21);
};
auto print_up_2_test = [](int, std::unique_ptr<test>& x) {
REQUIRE(x->special_value == 21);
};
auto print_sp_test = [](std::shared_ptr<test>& 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<test> ut = std::make_unique<test>(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<test>(21);
lua["v2"] = std::make_shared<test>(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);
auto result = lua.safe_script("f(v3)", sol::script_pass_on_error);
REQUIRE_FALSE(result.valid());
};
SECTION("throws-shared_ptr") {
sol::state lua;
lua.set_function("f", print_up_test);
lua["v2"] = std::make_shared<test>(44);
auto result = lua.safe_script("f(v2)", sol::script_pass_on_error);
REQUIRE_FALSE(result.valid());
};
SECTION("throws-ptr") {
sol::state lua;
lua.set_function("f", print_up_test);
lua["v4"] = ut.get();
auto result = lua.safe_script("f(v4)", sol::script_pass_on_error);
REQUIRE_FALSE(result.valid());
};
}
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<decltype(&raw_noexcept_function), &raw_noexcept_function>;
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);
}