diff --git a/README.md b/README.md index 43ed850..1dc8967 100644 --- a/README.md +++ b/README.md @@ -209,6 +209,8 @@ Thread result: 3 Effil allows to transmit data between threads (Lua interpreter states) using `effil.channel`, `effil.table` or directly as parameters of `effil.thread`. - Primitive types are transmitted 'as is' by copy: `nil`, `boolean`, `number`, `string` - Functions are dumped using [`lua_dump`](#https://www.lua.org/manual/5.3/manual.html#lua_dump). Upvalues are captured according to [the rules](#functions-upvalues). + - C functions (for which `lua_iscfunction` returns true) are transmitted just by a pointer using `lua_tocfunction` (in original lua_State) and lua_pushcfunction (in new lua_State). + **caution**: in LuaJIT standard functions like tonumber are not real C function, so `lua_iscfunction` returns true but `lua_tocfunction` returns nullptr. Due to that we don't find way to transmit it between lua_States. - **Userdata and Lua threads (coroutines)** are not supported. - Tables are serialized to `effil.table` recursively. So, any Lua table becomes `effil.table`. Table serialization may take a lot of time for big table. Thus, it's better to put data directly to `effil.table` avoiding a table serialization. Let's consider 2 examples: ```Lua diff --git a/src/cpp/stored-object.cpp b/src/cpp/stored-object.cpp index fca5a1d..c8a4a0d 100644 --- a/src/cpp/stored-object.cpp +++ b/src/cpp/stored-object.cpp @@ -108,7 +108,6 @@ class FunctionHolder : public GCObjectHolder { public: template FunctionHolder(const SolType& luaObject) : GCObjectHolder(luaObject) {} - FunctionHolder(GCHandle handle) : GCObjectHolder(handle) {} sol::object unpack(sol::this_state state) const final { return GC::instance().get(handle_).loadFunction(state); @@ -119,6 +118,29 @@ public: } }; +class CFunctionHolder : public BaseHolder { +public: + CFunctionHolder(sol::state_view state, int stack_index) + : BaseHolder() + { + cfunction_ = lua_tocfunction(state, stack_index); + REQUIRE(cfunction_ != nullptr) << "can't get C function pointer"; + } + + sol::object unpack(sol::this_state state) const final { + lua_pushcfunction(state, cfunction_); + return sol::stack::pop(state); + } + + bool rawCompare(const BaseHolder* other) const override { + const auto cfh = dynamic_cast(other); + return cfh && cfh->cfunction_ == cfunction_; + } + +private: + lua_CFunction cfunction_; +}; + void dumpTable(SharedTable& target, const sol::table& luaTable, SolTableToShared& visited); StoredObject makeStoredObject(const sol::object& luaObject, SolTableToShared& visited) { @@ -186,6 +208,11 @@ StoredObject fromSolObject(const SolObject& luaObject, SolTableToShared& visited else throw Exception() << "Unable to store userdata object"; case sol::type::function: { + { + auto poper = sol::stack::push_pop(luaObject); + if (lua_iscfunction(luaObject.lua_state(), -1)) + return std::make_unique(luaObject.lua_state(), -1); + } Function func = GC::instance().create(luaObject, visited); return std::make_unique(func.handle()); } diff --git a/tests/lua/function.lua b/tests/lua/function.lua new file mode 100644 index 0000000..8bacadf --- /dev/null +++ b/tests/lua/function.lua @@ -0,0 +1,43 @@ +require "bootstrap-tests" + +test.func.tear_down = default_tear_down + +test.func.check_truly_c_functions_p = function(func) + test.equal(type(func), "function") + + local t = effil.table() + local ret, _ = pcall(function() t["func"] = func end) + test.equal(ret, true) +end + +test.func.check_truly_c_functions_p(coroutine.create) +test.func.check_truly_c_functions_p(effil.size) + +test.func.check_tosting_c_functions = function() + local t = effil.table() + + if jit then + -- in LuaJIT it's not real C function + local ret, msg = pcall(function() t["tostring"] = tostring end) + test.equal(ret, false) + test.equal(msg, "effil.table: can't get C function pointer") + else + t["tostring"] = tostring + + if LUA_VERSION > 51 then + test.equal(t["tostring"], tostring) + else + test.not_equal(t["tostring"], tostring) + end -- LUA_VERSION > 51 + + test.equal(t["tostring"](123), tostring(123)) + test.equal(effil.thread(function() return t["tostring"](123) end)():get(), tostring(123)) + + local foo = tostring + test.equal(foo, tostring) + test.equal(foo(123), tostring(123)) + test.equal(effil.thread(function() return foo(123) end)():get(), tostring(123)) + end -- jit +end + + diff --git a/tests/lua/run_tests b/tests/lua/run_tests index 06d9808..db0ad42 100755 --- a/tests/lua/run_tests +++ b/tests/lua/run_tests @@ -1,6 +1,10 @@ #!/usr/bin/env lua -print("Using " .. _VERSION) +if jit then + print("Using LuaJIT") +else + print("Using " .. _VERSION) +end local scripts_path = arg[0]:gsub("[\\/]run_tests", "") local src_path = scripts_path .. "/../.." @@ -17,6 +21,7 @@ require "metatable" require "type_mismatch" require "upvalues" require "dump_table" +require "function" if os.getenv("STRESS") then require "channel-stress"