From 44a3f042a7a878af45ae893e0361d6a8be315da4 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Thu, 7 Jul 2016 04:44:36 +0200 Subject: [PATCH] prepare sharing C++ class instances across lua-interpreters in particular: lua-lifefime (!) C++ instances. This allows for dynamic allocation of custom user-data, bound to the lifetime of the allocating lua-context. --- libs/ardour/ardour/lua_api.h | 42 +++++++- libs/ardour/lua_api.cc | 150 +++++++++++++++++++++++++++ libs/ardour/luabindings.cc | 7 ++ libs/lua/LuaBridge/detail/LuaRef.h | 5 + libs/lua/LuaBridge/detail/Userdata.h | 14 +++ 5 files changed, 217 insertions(+), 1 deletion(-) diff --git a/libs/ardour/ardour/lua_api.h b/libs/ardour/ardour/lua_api.h index 9ae6bf2ff6..9af9649620 100644 --- a/libs/ardour/ardour/lua_api.h +++ b/libs/ardour/ardour/lua_api.h @@ -181,6 +181,46 @@ namespace ARDOUR { namespace LuaOSC { lo_address _addr; }; -} } /* namespace */ +} + +class LuaTableRef { + public: + LuaTableRef (); + ~LuaTableRef (); + + int get (lua_State* L); + int set (lua_State* L); + + private: + struct LuaTableEntry { + LuaTableEntry (int kt, int vt) + : keytype (kt) + , valuetype (vt) + { } + + int keytype; + std::string k_s; + unsigned int k_n; + + int valuetype; + // LUA_TUSERDATA + const void* c; + void* p; + // LUA_TBOOLEAN + bool b; + // LUA_TSTRING: + std::string s; + // LUA_TNUMBER: + double n; + }; + + std::vector _data; + + static void* findclasskey (lua_State *L, const void* key); + template + static void assign (luabridge::LuaRef* rv, T key, const LuaTableEntry& s); +}; + +} /* namespace */ #endif // _ardour_lua_api_h_ diff --git a/libs/ardour/lua_api.cc b/libs/ardour/lua_api.cc index 94b3f816c3..edee3a34a9 100644 --- a/libs/ardour/lua_api.cc +++ b/libs/ardour/lua_api.cc @@ -345,3 +345,153 @@ ARDOUR::LuaAPI::hsla_to_rgba (lua_State *L) luabridge::Stack::push (L, a); return 4; } + +luabridge::LuaRef::Proxy& +luabridge::LuaRef::Proxy::clone_instance (const void* classkey, void* p) { + lua_rawgeti (m_L, LUA_REGISTRYINDEX, m_tableRef); + lua_rawgeti (m_L, LUA_REGISTRYINDEX, m_keyRef); + + luabridge::UserdataPtr::push_raw (m_L, p, classkey); + + lua_rawset (m_L, -3); + lua_pop (m_L, 1); + return *this; +} + +LuaTableRef::LuaTableRef () {} +LuaTableRef::~LuaTableRef () {} + +int +LuaTableRef::get (lua_State* L) +{ + luabridge::LuaRef rv (luabridge::newTable (L)); + for (std::vector::const_iterator i = _data.begin (); i != _data.end (); ++i) { + switch ((*i).keytype) { + case LUA_TSTRING: + assign(&rv, i->k_s, *i); + break; + case LUA_TNUMBER: + assign(&rv, i->k_n, *i); + break; + } + } + luabridge::push (L, rv); + return 1; +} + +int +LuaTableRef::set (lua_State* L) +{ + if (!lua_istable (L, -1)) { return luaL_error (L, "argument is not a table"); } + _data.clear (); + + lua_pushvalue (L, -1); + lua_pushnil (L); + while (lua_next (L, -2)) { + lua_pushvalue (L, -2); + + LuaTableEntry s (lua_type(L, -1), lua_type(L, -2)); + switch (lua_type(L, -1)) { + case LUA_TSTRING: + s.k_s = luabridge::Stack::get (L, -1); + break; + ; + case LUA_TNUMBER: + s.k_n = luabridge::Stack::get (L, -1); + break; + default: + // invalid key + lua_pop (L, 2); + continue; + } + + switch(lua_type(L, -2)) { + case LUA_TSTRING: + s.s = luabridge::Stack::get (L, -2); + break; + case LUA_TBOOLEAN: + s.b = lua_toboolean (L, -2); + break; + case LUA_TNUMBER: + s.n = lua_tonumber (L, -2); + break; + case LUA_TUSERDATA: + { + bool ok = false; + lua_getmetatable (L, -2); + lua_rawgetp (L, -1, luabridge::getIdentityKey ()); + if (lua_isboolean (L, -1)) { + lua_pop (L, 1); + const void* key = lua_topointer (L, -1); + lua_pop (L, 1); + void const* classkey = findclasskey (L, key); + + if (classkey) { + ok = true; + s.c = classkey; + s.p = luabridge::Userdata::get_ptr (L, -2); + } + } else { + lua_pop (L, 2); + } + + if (ok) { + break; + } + // invalid userdata -- fall through + } + // no break + case LUA_TFUNCTION: // no support -- we could... string.format("%q", string.dump(value, true)) + case LUA_TTABLE: // no nested tables, sorry. + case LUA_TNIL: // fallthrough + default: + // invalid value + lua_pop (L, 2); + continue; + } + + _data.push_back(s); + lua_pop (L, 2); + } + return 0; +} + +void* +LuaTableRef::findclasskey (lua_State *L, const void* key) +{ + lua_pushvalue(L, LUA_REGISTRYINDEX); + lua_pushnil (L); + while (lua_next (L, -2)) { + lua_pushvalue (L, -2); + if (lua_topointer(L, -2) == key) { + void* rv = lua_touserdata (L, -1); + lua_pop (L, 4); + return rv; + } + lua_pop (L, 2); + } + lua_pop (L, 1); + return NULL; +} + +template +void LuaTableRef::assign (luabridge::LuaRef* rv, T key, const LuaTableEntry& s) +{ + switch (s.valuetype) { + case LUA_TSTRING: + (*rv)[key] = s.s; + break; + case LUA_TBOOLEAN: + (*rv)[key] = s.b; + break; + case LUA_TNUMBER: + (*rv)[key] = s.n; + break; + case LUA_TUSERDATA: + (*rv)[key].clone_instance (s.c, s.p); + break; + default: + assert (0); + break; + } +} diff --git a/libs/ardour/luabindings.cc b/libs/ardour/luabindings.cc index 393389c5ff..1ff2f39987 100644 --- a/libs/ardour/luabindings.cc +++ b/libs/ardour/luabindings.cc @@ -169,6 +169,7 @@ CLASSKEYS(ARDOUR::Session); CLASSKEYS(ARDOUR::BufferSet); CLASSKEYS(ARDOUR::ChanMapping); CLASSKEYS(ARDOUR::DSP::DspShm); +CLASSKEYS(ARDOUR::LuaTableRef); CLASSKEYS(PBD::ID); CLASSKEYS(ARDOUR::Location); CLASSKEYS(ARDOUR::PluginInfo); @@ -1373,6 +1374,12 @@ LuaBindings::dsp (lua_State* L) .endClass () .endNamespace () // DSP + + .beginClass ("LuaTableRef") + .addCFunction ("get", &LuaTableRef::get) + .addCFunction ("set", &LuaTableRef::set) + .endClass () + .endNamespace (); // ARDOUR } diff --git a/libs/lua/LuaBridge/detail/LuaRef.h b/libs/lua/LuaBridge/detail/LuaRef.h index 8fae1c038a..46c756445e 100644 --- a/libs/lua/LuaBridge/detail/LuaRef.h +++ b/libs/lua/LuaBridge/detail/LuaRef.h @@ -180,6 +180,11 @@ private: return *this; } + // the implementation needs UserdataPtr, which + // is not yet defined here. + // -> libs/ardour/lua_api.cc + Proxy& clone_instance (const void* key, void* p); + //-------------------------------------------------------------------------- /** Assign a new value to this table key. diff --git a/libs/lua/LuaBridge/detail/Userdata.h b/libs/lua/LuaBridge/detail/Userdata.h index e832ef2dc1..feab28f132 100644 --- a/libs/lua/LuaBridge/detail/Userdata.h +++ b/libs/lua/LuaBridge/detail/Userdata.h @@ -302,6 +302,11 @@ ud __parent (nil) public: virtual ~Userdata () { } + static void* get_ptr (lua_State* L, int index) { + Userdata* ud = static_cast (lua_touserdata (L, index)); + return ud->m_p; + } + //-------------------------------------------------------------------------- /** Returns the Userdata* if the class on the Lua stack matches. @@ -457,6 +462,15 @@ private: assert (m_p != 0); } + friend class LuaRef; + static inline void push_raw (lua_State* const L, void* p, const void* classkey) + { + new (lua_newuserdata (L, sizeof (UserdataPtr))) UserdataPtr (p); + lua_rawgetp (L, LUA_REGISTRYINDEX, classkey); + assert (lua_istable (L, -1)); + lua_setmetatable (L, -2); + } + public: /** Push non-const pointer to object. */