From 83bd028e8fc06a51f4a6cf9f779107ec5806a291 Mon Sep 17 00:00:00 2001 From: Lynix Date: Sun, 13 Dec 2015 03:29:57 +0100 Subject: [PATCH] Lua/LuaClass: Add inheritance (first implementation) Former-commit-id: 6c7f8d7dace5c857ae71958e15cc13d6a6f9ccb2 --- include/Nazara/Lua/LuaClass.hpp | 36 ++++--- include/Nazara/Lua/LuaClass.inl | 161 ++++++++++++++++++++++---------- 2 files changed, 135 insertions(+), 62 deletions(-) diff --git a/include/Nazara/Lua/LuaClass.hpp b/include/Nazara/Lua/LuaClass.hpp index fe592a4aa..16399fd3a 100644 --- a/include/Nazara/Lua/LuaClass.hpp +++ b/include/Nazara/Lua/LuaClass.hpp @@ -13,14 +13,16 @@ #include #include #include -//#include +#include +#include namespace Nz { - template + template class LuaClass { - //static_assert(std::is_same::value || std::is_base_of::value, "P must be a base of T"); + template + friend class LuaClass; public: using ClassFunc = std::function; @@ -32,7 +34,8 @@ namespace Nz LuaClass(const String& name); - //void Inherit(LuaClass

& parent); + template + void Inherit(LuaClass

& parent); void Register(LuaInstance& lua); @@ -44,7 +47,6 @@ namespace Nz void SetMethod(const String& name, ClassFunc method); template std::enable_if_t::value> SetMethod(const String& name, R(P::*func)(Args...), DefArgs... defArgs); template std::enable_if_t::value> SetMethod(const String& name, R(P::*func)(Args...) const, DefArgs... defArgs); - void SetBindMode(LuaBindMode mode); void SetSetter(ClassIndexFunc setter); void SetStaticGetter(StaticIndexFunc getter); void SetStaticMethod(const String& name, StaticFunc func); @@ -52,20 +54,15 @@ namespace Nz void SetStaticSetter(StaticIndexFunc getter); private: - static int ConstructorProxy(lua_State* state); - static int FinalizerProxy(lua_State* state); - static int InfoDestructor(lua_State* state); - static int GetterProxy(lua_State* state); - static int MethodProxy(lua_State* state); - static int SetterProxy(lua_State* state); - static int StaticGetterProxy(lua_State* state); - static int StaticMethodProxy(lua_State* state); - static int StaticSetterProxy(lua_State* state); + using ParentFunc = std::function; + using InstanceGetter = std::function; struct ClassInfo { std::vector methods; + std::vector parentGetters; std::vector staticMethods; + std::unordered_map instanceGetters; ClassIndexFunc getter = nullptr; ClassIndexFunc setter = nullptr; ConstructorFunc constructor = nullptr; @@ -76,6 +73,17 @@ namespace Nz int globalTableRef = -1; }; + static int ConstructorProxy(lua_State* state); + static int FinalizerProxy(lua_State* state); + static int InfoDestructor(lua_State* state); + static void Get(const std::shared_ptr& info, LuaInstance& lua, T& instance); + static int GetterProxy(lua_State* state); + static int MethodProxy(lua_State* state); + static int SetterProxy(lua_State* state); + static int StaticGetterProxy(lua_State* state); + static int StaticMethodProxy(lua_State* state); + static int StaticSetterProxy(lua_State* state); + std::map m_methods; std::map m_staticMethods; std::shared_ptr m_info; diff --git a/include/Nazara/Lua/LuaClass.inl b/include/Nazara/Lua/LuaClass.inl index afe89c702..79f92c385 100644 --- a/include/Nazara/Lua/LuaClass.inl +++ b/include/Nazara/Lua/LuaClass.inl @@ -4,6 +4,7 @@ #include #include +#include #include namespace Nz @@ -23,6 +24,26 @@ namespace Nz m_info->parentInfo = parent.m_info; } */ + + template + template + inline void LuaClass::Inherit(LuaClass

& parent) + { + static_assert(!std::is_same::value || std::is_base_of::value, "P must be a base of T"); + + std::shared_ptr::ClassInfo>& parentInfo = parent.m_info; + + parentInfo->instanceGetters[m_info->name] = [info = m_info](LuaInstance& lua) -> P* + { + return *static_cast(lua.CheckUserdata(1, info->name)); + }; + + m_info->parentGetters.emplace_back([parentInfo] (LuaInstance& lua, T& instance) + { + LuaClass

::Get(parentInfo, lua, instance); + }); + } + template void LuaClass::Register(LuaInstance& lua) { @@ -48,27 +69,32 @@ namespace Nz if (!lua.NewMetatable(m_info->name)) NazaraWarning("Class \"" + m_info->name + "\" already registred in this instance"); { - lua.PushValue(1); // On associe l'UserData avec la fonction - lua.PushCFunction(FinalizerProxy, 1); - lua.SetField("__gc"); // Finalizer + // Set the type in a __type field + lua.PushString(m_info->name); + lua.SetField("__type"); - if (m_info->getter) + // Define the Finalizer + lua.PushValue(1); + lua.PushCFunction(FinalizerProxy, 1); + lua.SetField("__gc"); + + if (m_info->getter || !m_info->parentGetters.empty()) { - lua.PushValue(1); - lua.PushValue(-2); + lua.PushValue(1); // shared_ptr on UserData + lua.PushValue(-2); // Metatable lua.PushCFunction(GetterProxy, 2); } else // Optimisation, plutôt que de rediriger vers une fonction C qui ne fera rien d'autre que rechercher // dans la table, nous envoyons directement la table, de sorte que Lua fasse directement la recherche // Ceci n'est possible que si nous n'avons ni getter, ni parent - lua.PushValue(-1); + lua.PushValue(-1); // Metatable lua.SetField("__index"); // Getter if (m_info->setter) { - lua.PushValue(1); + lua.PushValue(1); // shared_ptr on UserData lua.PushCFunction(SetterProxy, 1); lua.SetField("__newindex"); // Setter } @@ -76,14 +102,20 @@ namespace Nz m_info->methods.reserve(m_methods.size()); for (auto& pair : m_methods) { + std::size_t methodIndex = m_info->methods.size(); m_info->methods.push_back(pair.second); - lua.PushValue(1); - lua.PushInteger(m_info->methods.size() - 1); + lua.PushValue(1); // shared_ptr on UserData + lua.PushInteger(methodIndex); lua.PushCFunction(MethodProxy, 2); - lua.SetField(pair.first); // Méthode + lua.SetField(pair.first); // Method name } + + m_info->instanceGetters[m_info->name] = [info = m_info](LuaInstance& lua) + { + return *static_cast(lua.CheckUserdata(1, info->name)); + }; } lua.Pop(); // On pop la metatable @@ -104,21 +136,21 @@ namespace Nz if (m_info->staticGetter) { - lua.PushValue(1); - lua.PushValue(-2); + lua.PushValue(1); // shared_ptr on UserData + lua.PushValue(-2); // ClassMeta lua.PushCFunction(StaticGetterProxy, 2); } else // Optimisation, plutôt que de rediriger vers une fonction C qui ne fera rien d'autre que rechercher // dans la table, nous envoyons directement la table, de sorte que Lua fasse directement la recherche // Ceci n'est possible que si nous n'avons ni getter, ni parent - lua.PushValue(-1); + lua.PushValue(-1); // ClassMeta lua.SetField("__index"); // ClassMeta.__index = StaticGetterProxy/ClassMeta if (m_info->staticSetter) { - lua.PushValue(1); + lua.PushValue(1); // shared_ptr on UserData lua.PushCFunction(StaticSetterProxy, 1); lua.SetField("__newindex"); // ClassMeta.__newindex = StaticSetterProxy } @@ -126,10 +158,11 @@ namespace Nz m_info->staticMethods.reserve(m_staticMethods.size()); for (auto& pair : m_staticMethods) { + std::size_t methodIndex = m_info->staticMethods.size(); m_info->staticMethods.push_back(pair.second); - lua.PushValue(1); - lua.PushInteger(m_info->staticMethods.size() - 1); + lua.PushValue(1); // shared_ptr on UserData + lua.PushInteger(methodIndex); lua.PushCFunction(StaticMethodProxy, 2); lua.SetField(pair.first); // ClassMeta.method = StaticMethodProxy @@ -181,7 +214,7 @@ namespace Nz { SetMethod(name, [func, defArgs...] (LuaInstance& instance, T& object) -> int { - LuaImplMethodProxy::Impl handler(instance, object, defArgs...); + typename LuaImplMethodProxy::template Impl handler(instance, object, defArgs...); handler.ProcessArgs(); return handler.Invoke(func); @@ -243,8 +276,8 @@ namespace Nz { LuaInstance& lua = *LuaInstance::GetInstance(state); - ClassInfo* info = *static_cast(lua.ToUserdata(lua.GetIndexOfUpValue(1))); - ConstructorFunc constructor = info->constructor; + std::shared_ptr& info = *static_cast*>(lua.ToUserdata(lua.GetIndexOfUpValue(1))); + const ConstructorFunc& constructor = info->constructor; lua.Remove(1); // On enlève l'argument "table" du stack @@ -267,8 +300,8 @@ namespace Nz { LuaInstance& lua = *LuaInstance::GetInstance(state); - ClassInfo* info = *static_cast(lua.ToUserdata(lua.GetIndexOfUpValue(1))); - FinalizerFunc finalizer = info->finalizer; + std::shared_ptr& info = *static_cast*>(lua.ToUserdata(lua.GetIndexOfUpValue(1))); + const FinalizerFunc& finalizer = info->finalizer; T* instance = *static_cast(lua.CheckUserdata(1, info->name)); lua.Remove(1); //< Remove the instance from the Lua stack @@ -284,34 +317,54 @@ namespace Nz { LuaInstance& lua = *LuaInstance::GetInstance(state); - std::shared_ptr& infoPtr = *static_cast*>(lua.ToUserdata(lua.GetIndexOfUpValue(1))); - lua.DestroyReference(infoPtr->globalTableRef); + std::shared_ptr& info = *static_cast*>(lua.ToUserdata(lua.GetIndexOfUpValue(1))); + lua.DestroyReference(info->globalTableRef); using namespace std; // Obligatoire pour le destructeur - infoPtr.~shared_ptr(); // Si vous voyez une autre façon de faire, je suis preneur + info.~shared_ptr(); // Si vous voyez une autre façon de faire, je suis preneur return 0; } + template + void LuaClass::Get(const std::shared_ptr& info, LuaInstance& lua, T& instance) + { + const ClassIndexFunc& getter = info->getter; + + if (!getter || !getter(lua, instance)) + { + // Query from the metatable + lua.GetMetatable(info->name); //< Metatable + lua.PushValue(1); //< Field + lua.GetTable(); // Metatable[Field] + + lua.Remove(-2); // Remove Metatable + + if (!lua.IsValid(-1)) + { + for (const ParentFunc& getter : info->parentGetters) + { + lua.Pop(); //< Pop the last nil value + + getter(lua, instance); + if (lua.IsValid(-1)) + return; + } + } + } + } + template int LuaClass::GetterProxy(lua_State* state) { LuaInstance& lua = *LuaInstance::GetInstance(state); - ClassInfo* info = *static_cast(lua.ToUserdata(lua.GetIndexOfUpValue(1))); - ClassIndexFunc getter = info->getter; + std::shared_ptr& info = *static_cast*>(lua.ToUserdata(lua.GetIndexOfUpValue(1))); T& instance = *(*static_cast(lua.CheckUserdata(1, info->name))); lua.Remove(1); //< Remove the instance from the Lua stack - if (!getter(lua, instance)) - { - // On accède alors à la table - lua.PushValue(lua.GetIndexOfUpValue(2)); - lua.PushValue(-2); - lua.GetTable(); - } - + Get(info, lua, instance); return 1; } @@ -320,14 +373,26 @@ namespace Nz { LuaInstance& lua = *LuaInstance::GetInstance(state); - ClassInfo* info = *static_cast(lua.ToUserdata(lua.GetIndexOfUpValue(1))); - unsigned int index = static_cast(lua.ToInteger(lua.GetIndexOfUpValue(2))); - ClassFunc method = info->methods[index]; + std::shared_ptr& info = *static_cast*>(lua.ToUserdata(lua.GetIndexOfUpValue(1))); + + T* instance = nullptr; + if (lua.GetMetatable(1)) + { + LuaType type = lua.GetField("__type"); + if (type == LuaType_String) + { + String name = lua.ToString(-1); + auto it = info->instanceGetters.find(name); + if (it != info->instanceGetters.end()) + instance = it->second(lua); + } + } - T& instance = *(*static_cast(lua.CheckUserdata(1, info->name))); lua.Remove(1); //< Remove the instance from the Lua stack - return method(lua, instance); + unsigned int index = static_cast(lua.ToInteger(lua.GetIndexOfUpValue(2))); + const ClassFunc& method = info->methods[index]; + return method(lua, *instance); } template @@ -335,8 +400,8 @@ namespace Nz { LuaInstance& lua = *LuaInstance::GetInstance(state); - ClassInfo* info = *static_cast(lua.ToUserdata(lua.GetIndexOfUpValue(1))); - ClassIndexFunc setter = info->setter; + std::shared_ptr& info = *static_cast*>(lua.ToUserdata(lua.GetIndexOfUpValue(1))); + const ClassIndexFunc& setter = info->setter; T& instance = *(*static_cast(lua.CheckUserdata(1, info->name))); lua.Remove(1); //< Remove the instance from the Lua stack @@ -357,8 +422,8 @@ namespace Nz { LuaInstance& lua = *LuaInstance::GetInstance(state); - ClassInfo* info = *static_cast(lua.ToUserdata(lua.GetIndexOfUpValue(1))); - StaticIndexFunc getter = info->staticGetter; + std::shared_ptr& info = *static_cast*>(lua.ToUserdata(lua.GetIndexOfUpValue(1))); + const StaticIndexFunc& getter = info->staticGetter; if (!getter(lua)) { @@ -376,9 +441,9 @@ namespace Nz { LuaInstance& lua = *LuaInstance::GetInstance(state); - ClassInfo* info = *static_cast(lua.ToUserdata(lua.GetIndexOfUpValue(1))); + std::shared_ptr& info = *static_cast*>(lua.ToUserdata(lua.GetIndexOfUpValue(1))); unsigned int index = static_cast(lua.ToInteger(lua.GetIndexOfUpValue(2))); - StaticFunc method = info->staticMethods[index]; + const StaticFunc& method = info->staticMethods[index]; return method(lua); } @@ -388,8 +453,8 @@ namespace Nz { LuaInstance& lua = *LuaInstance::GetInstance(state); - ClassInfo* info = *static_cast(lua.ToUserdata(lua.GetIndexOfUpValue(1))); - StaticIndexFunc setter = info->staticSetter; + std::shared_ptr& info = *static_cast*>(lua.ToUserdata(lua.GetIndexOfUpValue(1))); + const StaticIndexFunc& setter = info->staticSetter; if (!setter(lua)) {