I'd like to define a function in Lua code and then use sol2 to bind that function to a module table. My initial idea was to use sol::function fn = lua.script("return function() return 5 end") and then tbl.set_function("fnName", fn), but it's behaving strangely; fnName will be of type function, but calling it will always return nil (note, though, that the function does get executed, it's just the returns that don't get retrieved).
I'm unsure if this sort of thing is meant to be possible in the first place, or if I'm binding it incorrectly, or what.
Test code (Lua v5.1.5, Sol2 v2.17.3):
#define SOL_CHECK_ARGUMENTS
#include <catch.hpp>
#include <sol.hpp>
TEST_CASE("setting a function returned from Lua code")
{
sol::state lua;
lua.open_libraries(sol::lib::base);
sol::function fn = lua.script("return function() return 5 end");
// this works as expected
SECTION("getting the value from C++")
{
int result = fn();
REQUIRE(result == 5);
}
// this won't work, the return is nil
SECTION("setting the function directly")
{
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)"));
}
// this will print 'this was executed'
SECTION("does the function actually get executed?")
{
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()"));
}
// this works
SECTION("setting the function indirectly, with the return value cast explicitly")
{
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)"));
}
}
Results:
-------------------------------------------------------------------------------
setting a function returned from Lua code
setting the function directly
-------------------------------------------------------------------------------
..\module_function.cpp(18)
...............................................................................
..\module_function.cpp(23): FAILED:
REQUIRE_NOTHROW( lua.script("assert(test() ~= nil)") )
due to unexpected exception with message:
lua: error: [string "assert(test() ~= nil)"]:1: assertion failed!
this was executed
This exposes what is technically a bug in the set_function path. set_function is meant to be used with C++ functions and does its damndest to deduce return type, arguments, and other signature information, as well as bundle it up in a way that's usable. What it does not account for is someone having a sol::function already.
Your code would work just by doing lua.set( "test", fn ); But, I still consider it an oversight for set_function to not handle what is sol2's basic functional type. I'll have to patch this in and backport it into 2.17.3. Thanks for reporting.
Ah, cool, thanks for the clarification. set does indeed work.
Backported, your original code will work: