In my last post I covered restoring the LLVM JIT compiler support in the NervLand project. That part seems to be working not too bad for now, but I realized that if I want to be able to dynamically inject new functions in the lua state then I need a more user friendly way to do it still using the Luna infrastructure, but with a mechanism a bit similar to what is done in the SOL library. So this will be the main topic that we will cover here. Let's get started [Spoiler alert: this is going to be a little tricky ]
int add(int a, int b) { return a+b; } void register_function(LuaState& state) { state.set_function("nvk.add_func", add); }
local res = nvk.add_func(40, 2) CHECK(res==42, "Unexpected result")
#define LUNA_DECLARE_REGISTRAR() \ namespace luna { \ auto get_bind_functions() -> LuaBindFunctionList&; \ inline void register_bind_function(nv::LuaManager::BindingFunc func) { \ get_bind_functions().push_back(func); \ } \ struct BindingRegistration { \ BindingRegistration(nv::LuaManager::BindingFunc func) { \ register_bind_function(func); \ } \ }; } #define LUNA_IMPLEMENT_REGISTRAR() \ namespace luna { \ auto get_bind_functions() -> LuaBindFunctionList& { \ static LuaBindFunctionList bindFuncs; \ return bindFuncs; \ } \ } #define LUNA_ADD_BINDING(func) \ static const luna::BindingRegistration loader{func};
#include <sandbox_precomp.h> using namespace nv; static auto add(int a, int b) -> int { return a + b; } static void load_bindings(LuaState& lua) { logDEBUG("Should register my add function in the lua state here."); } LUNA_ADD_BINDING(load_bindings)
2022-11-26 13:13:24.719107 [DEBUG] Loading Sandbox bindings... 2022-11-26 13:13:24.719136 [DEBUG] Loading bindings for Sandbox with libname luaSandbox 2022-11-26 13:13:24.719160 [DEBUG] Loading dynamic library: luaSandbox.dll 2022-11-26 13:13:24.719182 [DEBUG] NervApp: load_library... 2022-11-26 13:13:24.719192 [DEBUG] lib file: modules/luaSandbox.dll 2022-11-26 13:13:24.746891 [DEBUG] Registering bindings for Sandbox 2022-11-26 13:13:24.746964 [DEBUG] Opened DynamicLibrary modules/luaSandbox.dll 2022-11-26 13:13:24.747013 [DEBUG] Should register my add function in the lua state here.
struct NVCORE_EXPORT LuaTable { using LuaCFunc = int (*)(lua_State*); LuaTable(const LuaTable&) = delete; auto operator=(const LuaTable&) -> LuaTable& = delete; LuaTable(LuaTable&&) noexcept; auto operator=(LuaTable&&) noexcept -> LuaTable&; lua_State* state{nullptr}; int index{0}; bool isRef{false}; LuaTable() = delete; ~LuaTable(); LuaTable(lua_State* L, int idx, bool makeRef); void push_self() const; [[nodiscard]] auto get_or_create_table(const nv::String& tname, nv::U64 offset = 0) const -> LuaTable; void set_function(const char* key, LuaCFunc func) const; void set_closure(const char* key, LuaCFunc func, nv::I32 nargs) const; auto operator[](const char* key) -> LuaTableSlot; };
struct NVCORE_EXPORT LuaTableSlot { LuaTableSlot(const LuaTableSlot&) = default; auto operator=(const LuaTableSlot&) -> LuaTableSlot& = default; LuaTableSlot(LuaTableSlot&&) = default; auto operator=(LuaTableSlot&&) -> LuaTableSlot& = default; LuaTableSlot(LuaTable* parent, const char* keyStr) : table(parent), key(keyStr){}; ~LuaTableSlot() = default; template <typename T> auto operator=(T func) -> LuaTableSlot& { // Apply the connection: LuaConnector<T>::connect(*table, key, func); return *this; } LuaTable* table{nullptr}; const char* key; };
template <typename T> struct LuaExecutor; template <typename T> struct LuaConnector { static void connect(LuaTable& table, const char* key, T func) { lua_State* L = table.state; lua_pushlightuserdata(L, (void*)func); table.set_closure(key, LuaExecutor<T>::call, 1); } };
template <typename Ret, typename A0> struct LuaExecutor<Ret (*)(A0)> { using func_t = Ret (*)(A0); static auto call(lua_State* L) -> int { // Retrieve the function as an upvalue: auto func = (func_t)lua_touserdata(L, lua_upvalueindex(1)); A0 arg0 = luna::LunaGetter<A0>::getValue(L, 1); Ret val = func(arg0); LunaPusher<Ret>::pushValue(L, val); return 1; } };
template <unsigned int N, typename T, typename... R> struct type_at { using type = typename type_at<N - 1, R...>::type; }; template <typename T, typename... R> struct type_at<0, T, R...> { using type = T; }; // cf. http://loungecpp.wikidot.com/tips-and-tricks:indices template <std::size_t... Is> struct indices {}; template <std::size_t N, std::size_t... Is> struct build_indices : build_indices<N - 1, N - 1, Is...> {}; template <std::size_t... Is> struct build_indices<0, Is...> : indices<Is...> {}; template <typename... Args> struct LuaExecutor<void (*)(Args...)> { using func_t = void (*)(Args...); static constexpr size_t nArgs = sizeof...(Args); static constexpr build_indices<nArgs> indices{}; template <unsigned int idx> static auto get_lua_value(lua_State* L) -> typename type_at<idx, Args...>::type { return luna::LunaGetter<typename type_at<idx, Args...>::type>::getValue( L, idx + 1); } template <std::size_t... Is> static void call_func(func_t func, lua_State* L, const luna::indices<Is...>& /*ids*/) { func(get_lua_value<Is>(L)...); } static auto call(lua_State* L) -> int { // Retrieve the function as an upvalue: auto func = (func_t)lua_touserdata(L, lua_upvalueindex(1)); // How to retrieve the arguments here ? call_func(func, L, indices); return 0; } }; template <typename Ret, typename... Args> struct LuaExecutor<Ret (*)(Args...)> { using func_t = Ret (*)(Args...); static constexpr size_t nArgs = sizeof...(Args); // using indices = build_indices<nArgs>; static constexpr build_indices<nArgs> indices{}; template <unsigned int idx> static auto get_lua_value(lua_State* L) -> typename type_at<idx, Args...>::type { return luna::LunaGetter<typename type_at<idx, Args...>::type>::getValue( L, idx + 1); } template <std::size_t... Is> static auto call_func(func_t func, lua_State* L, const luna::indices<Is...>& /*ids*/) -> Ret { return func(get_lua_value<Is>(L)...); } static auto call(lua_State* L) -> int { // Retrieve the function as an upvalue: auto func = (func_t)lua_touserdata(L, lua_upvalueindex(1)); Ret val = call_func(func, L, indices); LunaPusher<Ret>::pushValue(L, val); return 1; } };
template <std::size_t... Is> struct indices {}; template <std::size_t N, std::size_t... Is> struct build_indices : build_indices<N - 1, N - 1, Is...> {}; template <std::size_t... Is> struct build_indices<0, Is...> : indices<Is...> {}; template <typename Ret, typename... Args> struct LuaCaller<Ret (*)(Args...)> { using func_t = Ret (*)(Args...); static auto call(lua_State* L) -> int { return LuaExecutor<func_t>::call_ret_args(L); } }; template <typename... Args> struct LuaCaller<void (*)(Args...)> { using func_t = void (*)(Args...); static auto call(lua_State* L) -> int { return LuaExecutor<func_t>::call_void_args(L); } }; template <typename Ret> struct LuaCaller<Ret (*)()> { using func_t = Ret (*)(); static auto call(lua_State* L) -> int { auto func = (func_t)lua_touserdata(L, lua_upvalueindex(1)); Ret val = func(); LunaPusher<Ret>::pushValue(L, val); return 1; } }; template <> struct LuaCaller<void (*)()> { using func_t = void (*)(); static auto call(lua_State* L) -> int { auto func = (func_t)lua_touserdata(L, lua_upvalueindex(1)); func(); return 0; } }; template <typename Ret, typename... Args> struct LuaExecutor<Ret (*)(Args...)> { using func_t = Ret (*)(Args...); static constexpr size_t nArgs = sizeof...(Args); static constexpr build_indices<nArgs> indices{}; template <unsigned int idx> static auto get_lua_value(lua_State* L) -> typename type_at<idx, Args...>::type { return luna::LunaGetter<typename type_at<idx, Args...>::type>::getValue( L, idx + 1); } template <std::size_t... Is> static auto call_func(func_t func, lua_State* L, const luna::indices<Is...>& /*ids*/) -> Ret { return func(get_lua_value<Is>(L)...); } static auto call_ret_args(lua_State* L) -> int { // Retrieve the function as an upvalue: auto func = (func_t)lua_touserdata(L, lua_upvalueindex(1)); Ret val = call_func(func, L, indices); LunaPusher<Ret>::pushValue(L, val); return 1; } static auto call_void_args(lua_State* L) -> int { // Retrieve the function as an upvalue: auto func = (func_t)lua_touserdata(L, lua_upvalueindex(1)); call_func(func, L, indices); return 0; } };
#include <sandbox_precomp.h> using namespace nv; static auto meaning() -> int { logDEBUG("Returning meaning of life."); return 42; }; static auto square2(int a) -> int { return a * a; } static auto add(int a, int b) -> int { return a + b; } static void load_bindings(LuaState& lua) { logDEBUG("Should register my add function in the lua state here."); auto space = lua.get_or_create_table("tests"); space["meaning"] = meaning; space["square"] = square2; space["add"] = add; space["sub"] = [](int a, int b) { return a - b; }; } LUNA_ADD_BINDING(load_bindings)
static void load_bindings(LuaState& lua) { logDEBUG("Should register my add function in the lua state here."); auto space = lua.get_or_create_table("tests"); space["meaning"] = meaning; space["square"] = square2; space["add"] = add; auto func = [](int a, int b) { return a - b; }; space["sub"] = (int (*)(int, int))func; }
template <typename T> struct PtrType; template <typename Ret, typename C, typename... Args> struct PtrType<Ret (C::*)(Args...) const> { using func_t = Ret (*)(Args...); }; template <typename T> struct LuaConnector { using op_t = decltype(&T::operator()); using func_t = typename PtrType<op_t>::func_t; static void connect(LuaTable& table, const char* key, T func) { lua_State* L = table.state; lua_pushlightuserdata(L, (void*)(func_t)func); table.set_closure(key, LuaCaller<func_t>::call, 1); } }; template <typename Ret, typename... Args> struct LuaConnector<Ret (*)(Args...)> { using func_t = Ret (*)(Args...); static void connect(LuaTable& table, const char* key, func_t func) { lua_State* L = table.state; lua_pushlightuserdata(L, (void*)(func_t*)func); table.set_closure(key, LuaCaller<func_t>::call, 1); } };
if not cl:isInnerClass() and not cl:isUnion() and not cname:find("<") and not nsIsClass and not isTemplate then self:writeSubLine("class ${1};", cname) else -- Get the header for this "class": local tgtcl = cl:isInnerClass() and cl:getParent() or cl local hfile = self:getHeaderForClass(tgtcl) if hfile ~= nil then logDEBUG("Traits: adding required header: ", hfile) required_headers:insert(hfile) else logDEBUG("Traits: No header file found for definition of: ", cfname) end end
D:\Projects\NervLand\sources\nvCore\src\lua\luna.h:257:56: error: no type named 'root_t' in 'luna::LunaTraits<const nv::ByteArray>' using provider_t = LunaProvider<typename traits_t::root_t>; ~~~~~~~~~~~~~~~~~~~^~~~~~ D:\Projects\NervLand\sources\nvCore\src\lua\luna.h:722:17: note: in instantiation of template class 'luna::Luna<const nv::ByteArray>' requested here return *Luna<T>::get(L, idx, false); ^ D:\Projects\NervLand\sources\nvCore\src\lua\LuaTable.h:158:72: note: in instantiation of member function 'luna::LunaGetter<const nv::ByteArray &>::getValue' requested here return luna::LunaGetter<typename type_at<idx, Args...>::type>::getValue()...
template <typename T> struct LunaGetter; template <typename T> struct LunaGetter<T*> { static auto getValue(lua_State* L, int idx) -> T* { return Luna<T>::get(L, idx, true); } }; template <typename T> struct LunaGetter<const T*> { static auto getValue(lua_State* L, int idx) -> T* { return Luna<T>::get(L, idx, true); } }; template <typename T> struct LunaGetter<T&> { static auto getValue(lua_State* L, int idx) -> T& { return *Luna<T>::get(L, idx, false); } }; template <typename T> struct LunaGetter<const T&> { static auto getValue(lua_State* L, int idx) -> T& { return *Luna<T>::get(L, idx, false); } };
self.cbufs, self.pushArr)
self.renderer:set_cmd_buffer_provider(prov)
2022-12-02 22:48:47.492428 [DEBUG] Closing DynamicLibrary modules/luaSandbox.dll 2022-12-02 22:48:47.492461 [DEBUG] Unregistering bindings for Sandbox 2022-12-02 22:48:47.493178 [DEBUG] Destroying LuaManager... 2022-12-02 22:48:47.493203 [DEBUG] Destroying memory manager. Deleted LogManager object. Looking for memory leaks... [FATAL] Found 1 memory leaks!!! [FATAL] Leaking object 0000019E7A93B450 with count: 4294967295 Exiting.