diff --git a/CMakeLists.txt b/CMakeLists.txt index 0ea64a4..f33e5c8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,12 +44,12 @@ add_library(effil SHARED ${SOURCES}) target_link_libraries(effil ${LUA_LIBRARY}) if (WIN32) set_target_properties(effil PROPERTIES - PREFIX lib + PREFIX "" SUFFIX .dll) else() target_link_libraries(effil -lpthread -ldl) set_target_properties(effil PROPERTIES - PREFIX lib + PREFIX "" SUFFIX .so) endif() @@ -66,10 +66,6 @@ endif() #---------- # INSTALL - #---------- -install(FILES src/lua/effil.lua - DESTINATION ${CMAKE_INSTALL_PREFIX} - PERMISSIONS OWNER_READ GROUP_READ WORLD_READ) - install(TARGETS effil DESTINATION ${CMAKE_INSTALL_PREFIX} PERMISSIONS OWNER_READ GROUP_READ WORLD_READ) diff --git a/src/cpp/lua-module.cpp b/src/cpp/lua-module.cpp index c66df4c..cabd934 100644 --- a/src/cpp/lua-module.cpp +++ b/src/cpp/lua-module.cpp @@ -9,15 +9,6 @@ using namespace effil; namespace { -sol::object createThread(const sol::this_state& lua, - const std::string& path, - const std::string& cpath, - int step, - const sol::function& function, - const sol::variadic_args& args) { - return sol::make_object(lua, GC::instance().create(path, cpath, step, function, args)); -} - sol::object createTable(sol::this_state lua, const sol::optional& tbl) { if (tbl) { @@ -34,41 +25,94 @@ sol::object createChannel(const sol::stack_object& capacity, sol::this_state lua SharedTable globalTable = GC::instance().create(); -std::string getLuaTypename(const sol::stack_object& obj) -{ +std::string getLuaTypename(const sol::stack_object& obj) { return luaTypename<>(obj); } +size_t luaSize(const sol::stack_object& obj) { + if (obj.is()) + return SharedTable::luaSize(obj); + else if (obj.is()) + return obj.as().size(); + + throw effil::Exception() << "Unsupported type " + << luaTypename(obj) << " for effil.size()"; +} + +sol::table luaThreadConfig(sol::this_state state, const sol::stack_object& obj) { + REQUIRE(obj.valid() && obj.get_type() == sol::type::function) + << "bad argument #1 to 'effil.thread' (function expected, got " + << luaTypename(obj) << ")"; + + auto lua = sol::state_view(state); + const sol::function func = obj.as(); + + auto config = lua.create_table_with( + "path", lua["package"]["path"], + "cpath", lua["package"]["cpath"], + "step", 200 + ); + + auto meta = lua.create_table_with(); + meta[sol::meta_function::call] = [func](sol::this_state lua, + const sol::stack_table& self, const sol::variadic_args& args) + { + return sol::make_object(lua, GC::instance().create( + self["path"], self["cpath"], self["step"], func, args)); + }; + + config[sol::metatable_key] = meta; + return config; +} + } // namespace extern "C" #ifdef _WIN32 __declspec(dllexport) #endif -int luaopen_libeffil(lua_State* L) { +int luaopen_effil(lua_State* L) { sol::state_view lua(L); Thread::exportAPI(lua); SharedTable::exportAPI(lua); Channel::exportAPI(lua); - sol::table publicApi = lua.create_table_with( - "thread", createThread, - "thread_id", threadId, - "sleep", sleep, - "yield", yield, - "table", createTable, - "rawset", SharedTable::luaRawSet, - "rawget", SharedTable::luaRawGet, - "table_size", SharedTable::luaSize, + + const sol::table gcApi = GC::exportAPI(lua); + const sol::object gLuaTable = sol::make_object(lua, globalTable); + + const auto luaIndex = [gcApi, gLuaTable]( + const sol::stack_object& obj, const std::string& key) -> sol::object + { + if (key == "G") + return gLuaTable; + else if (key == "gc") + return gcApi; + else if (key == "version") + return sol::make_object(obj.lua_state(), "0.1.0"); + return sol::nil; + }; + + sol::usertype type("new", sol::no_constructor, + "thread", luaThreadConfig, + "thread_id", threadId, + "sleep", sleep, + "yield", yield, + "table", createTable, + "rawset", SharedTable::luaRawSet, + "rawget", SharedTable::luaRawGet, "setmetatable", SharedTable::luaSetMetatable, "getmetatable", SharedTable::luaGetMetatable, - "G", sol::make_object(lua, globalTable), - "gc", GC::exportAPI(lua), - "channel", createChannel, - "type", getLuaTypename, - "pairs", SharedTable::globalLuaPairs, - "ipairs", SharedTable::globalLuaIPairs, - "allow_table_upvalues", luaAllowTableUpvalues + "channel", createChannel, + "type", getLuaTypename, + "pairs", SharedTable::globalLuaPairs, + "ipairs", SharedTable::globalLuaIPairs, + "size", luaSize, + "allow_table_upvalues", luaAllowTableUpvalues, + sol::meta_function::index, luaIndex ); - sol::stack::push(lua, publicApi); + + sol::stack::push(lua, type); + sol::stack::pop(lua); + sol::stack::push(lua, EffilApiMarker()); return 1; } diff --git a/src/cpp/stored-object.cpp b/src/cpp/stored-object.cpp index 8bb415a..3c73f11 100644 --- a/src/cpp/stored-object.cpp +++ b/src/cpp/stored-object.cpp @@ -15,6 +15,15 @@ namespace effil { namespace { +class ApiReferenceHolder : public BaseHolder { +public: + bool rawCompare(const BaseHolder*) const noexcept final { return true; } + sol::object unpack(sol::this_state lua) const final { + luaopen_effil(lua); + return sol::stack::pop(lua); + } +}; + class NilHolder : public BaseHolder { public: bool rawCompare(const BaseHolder*) const noexcept final { return true; } @@ -163,6 +172,8 @@ StoredObject fromSolObject(const SolObject& luaObject) { return std::make_unique(luaObject); else if (luaObject.template is()) return std::make_unique>(luaObject); + else if (luaObject.template is()) + return std::make_unique(); else throw Exception() << "Unable to store userdata object"; case sol::type::function: { diff --git a/src/cpp/stored-object.h b/src/cpp/stored-object.h index da0f138..0f33865 100644 --- a/src/cpp/stored-object.h +++ b/src/cpp/stored-object.h @@ -7,6 +7,8 @@ namespace effil { +struct EffilApiMarker{}; + // Represents an interface for lua type stored at C++ code class BaseHolder { public: diff --git a/src/cpp/utils.h b/src/cpp/utils.h index d21695e..b9b1656 100644 --- a/src/cpp/utils.h +++ b/src/cpp/utils.h @@ -16,6 +16,12 @@ # define LUA_INDEX_TYPE lua_Number #endif +extern "C" +#ifdef _WIN32 + __declspec(dllexport) +#endif +int luaopen_effil(lua_State* L); + namespace effil { class Exception : public sol::error { diff --git a/src/lua/effil.lua b/src/lua/effil.lua deleted file mode 100644 index 38cd591..0000000 --- a/src/lua/effil.lua +++ /dev/null @@ -1,56 +0,0 @@ -local capi = require 'libeffil' - -local api = { - version = "0.1.0", - table = capi.table, - thread_id = capi.thread_id, - sleep = capi.sleep, - yield = capi.yield, - rawget = capi.rawget, - rawset = capi.rawset, - setmetatable = capi.setmetatable, - getmetatable = capi.getmetatable, - G = capi.G, - gc = capi.gc, - channel = capi.channel, - type = capi.type, - pairs = capi.pairs, - ipairs = capi.ipairs, - allow_table_upvalues = capi.allow_table_upvalues -} - -api.size = function (something) - local t = api.type(something) - if t == "effil.table" then - return capi.table_size(something) - elseif t == "effil.channel" then - return something:size() - else - error("Unsupported type " .. t .. " for effil.size()") - end -end - -local function run_thread(config, f, ...) - return capi.thread(config.path, config.cpath, config.step, f, ...) -end - --- Creates thread runner with given function --- configurable parameters: --- path - lua modules search path in child thread --- cpath - lua libs search path in child thread --- step - who fast reacte on state changing --- __call - run thread, can be invoked multiple times -api.thread = function (f) - if type(f) ~= "function" then - error("bad argument #1 to 'effil.thread' (function expected, got " .. capi.type(f) .. ")") - end - - local thread_config = { - path = package.path, - cpath = package.cpath, - step = 200 } - setmetatable(thread_config, {__call = function(c, ...) return run_thread(c, f, ...) end}) - return thread_config -end - -return api \ No newline at end of file diff --git a/tests/lua/thread.lua b/tests/lua/thread.lua index ba7ca29..b4df6ae 100644 --- a/tests/lua/thread.lua +++ b/tests/lua/thread.lua @@ -1,5 +1,7 @@ require "bootstrap-tests" +local effil = effil + test.thread.tear_down = default_tear_down test.thread.wait = function () @@ -26,7 +28,7 @@ end test.thread.timed_get = function () local thread = effil.thread(function() - require('effil').sleep(2) + effil.sleep(2) return "-_-" end)() test.is_nil(thread:get(1)) @@ -35,7 +37,7 @@ end test.thread.timed_get = function () local thread = effil.thread(function() - require('effil').sleep(2) + effil.sleep(2) return 8 end)() @@ -50,7 +52,7 @@ end test.thread.async_wait = function() local thread = effil.thread( function() - require('effil').sleep(1) + effil.sleep(1) end)() local iter = 0 @@ -86,7 +88,7 @@ test.thread.cancel = function () jit ~= nil and function() while true do - require("effil").yield() + effil.yield() end end or @@ -218,7 +220,7 @@ end test.thread.timed_cancel = function () local thread = effil.thread(function() - require("effil").sleep(4) + effil.sleep(4) end)() test.is_false(thread:cancel(100, "ms")) thread:wait() @@ -283,7 +285,7 @@ test.this_thread.functions = function () local thread_factory = effil.thread( function(share) - share["child.id"] = require('effil').thread_id() + share["child.id"] = effil.thread_id() end ) local thread = thread_factory(share) @@ -297,12 +299,12 @@ end test.this_thread.cancel_with_yield = function () local share = effil.table() local spec = effil.thread(function (share) - require('effil').sleep(1) + effil.sleep(1) for i=1,10000 do -- Just waiting end share.done = true - require("effil").yield() + effil.yield() share.afet_yield = true end) spec.step = 0 @@ -318,7 +320,7 @@ test.this_thread.pause_with_yield = function () local share = effil.table({stop = false}) local spec = effil.thread(function (share) while not share.stop do - require("effil").yield() + effil.yield() end share.done = true return true @@ -339,7 +341,7 @@ test.this_thread.pause_with_yield = function () end local function worker(cmd) - eff = require("effil") + eff = effil while not cmd.need_to_stop do eff.yield() end