diff --git a/src/lua/bridge.cpp b/src/lua/bridge.cpp index ba6f50b4..a45f2b27 100644 --- a/src/lua/bridge.cpp +++ b/src/lua/bridge.cpp @@ -110,16 +110,32 @@ static auto new_property_module(lua_State* state) -> int { return 1; } +template +inline constexpr bool always_false_v = false; + auto Bridge::AddPropertyModule( const std::string& name, - std::vector>> props) - -> void { + std::vector>>> + props) -> void { // Create the module (or retrieve it if one with this name already exists) luaL_requiref(&state_, name.c_str(), new_property_module, true); for (const auto& prop : props) { lua_pushstring(&state_, prop.first.c_str()); - bindings_.Register(&state_, prop.second.get()); + std::visit( + [&](auto&& arg) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + bindings_.Register(&state_, arg); + } else if constexpr (std::is_same_v>) { + bindings_.Register(&state_, arg.get()); + } else { + static_assert(always_false_v, "missing case"); + } + }, + prop.second); + lua_settable(&state_, -3); // metatable.propname = property } diff --git a/src/lua/include/bridge.hpp b/src/lua/include/bridge.hpp index 26401d14..91153d67 100644 --- a/src/lua/include/bridge.hpp +++ b/src/lua/include/bridge.hpp @@ -24,7 +24,10 @@ class Bridge { auto AddPropertyModule( const std::string&, - std::vector>>) -> void; + std::vector< + std::pair>>>) + -> void; system_fsm::ServiceLocator& services() { return services_; } PropertyBindings& bindings() { return bindings_; } diff --git a/src/lua/include/property.hpp b/src/lua/include/property.hpp index b6b4718f..60f9906a 100644 --- a/src/lua/include/property.hpp +++ b/src/lua/include/property.hpp @@ -16,6 +16,7 @@ namespace lua { using LuaValue = std::variant; +using LuaFunction = std::function; class Property { public: @@ -42,6 +43,12 @@ class PropertyBindings { PropertyBindings(lua_State&); auto Register(lua_State*, Property*) -> void; + auto Register(lua_State*, LuaFunction) -> void; + + auto GetFunction(size_t i) -> const LuaFunction&; + + private: + std::vector functions_; }; } // namespace lua diff --git a/src/lua/property.cpp b/src/lua/property.cpp index 3130077b..353e01ae 100644 --- a/src/lua/property.cpp +++ b/src/lua/property.cpp @@ -5,10 +5,12 @@ */ #include "property.hpp" +#include #include #include +#include "lauxlib.h" #include "lua.h" #include "lua.hpp" #include "lvgl.h" @@ -16,11 +18,13 @@ namespace lua { -static const char kMetatableName[] = "property"; +static const char kPropertyMetatable[] = "property"; +static const char kFunctionMetatable[] = "function"; static const char kBindingsTable[] = "bindings"; +static const char kBinderKey[] = "binder"; static auto check_property(lua_State* state) -> Property* { - void* data = luaL_checkudata(state, 1, kMetatableName); + void* data = luaL_checkudata(state, 1, kPropertyMetatable); luaL_argcheck(state, data != NULL, 1, "`property` expected"); return *reinterpret_cast(data); } @@ -71,9 +75,25 @@ static const struct luaL_Reg kPropertyBindingFuncs[] = {{"get", property_get}, {"bind", property_bind}, {NULL, NULL}}; +static auto generic_function_cb(lua_State* state) -> int { + lua_pushstring(state, kBinderKey); + lua_gettable(state, LUA_REGISTRYINDEX); + PropertyBindings* binder = + reinterpret_cast(lua_touserdata(state, -1)); + + size_t* index = + reinterpret_cast(luaL_checkudata(state, 1, kFunctionMetatable)); + const LuaFunction& fn = binder->GetFunction(*index); + return std::invoke(fn, state); +} + PropertyBindings::PropertyBindings(lua_State& s) { + lua_pushstring(&s, kBinderKey); + lua_pushlightuserdata(&s, this); + lua_settable(&s, LUA_REGISTRYINDEX); + // Create the metatable responsible for the Property API. - luaL_newmetatable(&s, kMetatableName); + luaL_newmetatable(&s, kPropertyMetatable); lua_pushliteral(&s, "__index"); lua_pushvalue(&s, -2); @@ -94,16 +114,38 @@ PropertyBindings::PropertyBindings(lua_State& s) { lua_setmetatable(&s, -2); // setmetatable(bindings, meta) lua_settable(&s, LUA_REGISTRYINDEX); // REGISTRY[kBindingsTable] = bindings + + // Create the metatable for C++ functions. + luaL_newmetatable(&s, kFunctionMetatable); + + lua_pushliteral(&s, "__call"); + lua_pushcfunction(&s, generic_function_cb); + lua_settable(&s, -3); // metatable.__call = metatable + + lua_pop(&s, 1); // Clean up the function metatable } auto PropertyBindings::Register(lua_State* s, Property* prop) -> void { Property** data = - reinterpret_cast(lua_newuserdata(s, sizeof(Property*))); + reinterpret_cast(lua_newuserdata(s, sizeof(uintptr_t))); *data = prop; - luaL_setmetatable(s, kMetatableName); + luaL_setmetatable(s, kPropertyMetatable); } +auto PropertyBindings::Register(lua_State* s, LuaFunction fn) -> void { + size_t* index = reinterpret_cast(lua_newuserdata(s, sizeof(size_t))); + *index = functions_.size(); + functions_.push_back(fn); + + luaL_setmetatable(s, kFunctionMetatable); +} + +auto PropertyBindings::GetFunction(size_t i) -> const LuaFunction& { + assert(i < functions_.size()); + return functions_[i]; +}; + template inline constexpr bool always_false_v = false;