diff --git a/include/Nazara/Lua/LuaClass.hpp b/include/Nazara/Lua/LuaClass.hpp index 24f41fef6..bb75569fe 100644 --- a/include/Nazara/Lua/LuaClass.hpp +++ b/include/Nazara/Lua/LuaClass.hpp @@ -24,6 +24,8 @@ class NzLuaClass using ClassIndexFunc = bool (*)(NzLuaInstance& lua, T& instance); using ConstructorFunc = T* (*)(NzLuaInstance& lua); using FinalizerFunc = bool (*)(NzLuaInstance& lua, T& instance); + using StaticIndexFunc = bool (*)(NzLuaInstance& lua); + using StaticFunc = int (*)(NzLuaInstance& lua); NzLuaClass(const NzString& name); @@ -31,11 +33,16 @@ class NzLuaClass void Register(NzLuaInstance& lua); + void PushGlobalTable(NzLuaInstance& lua); + void SetConstructor(ConstructorFunc constructor); void SetFinalizer(FinalizerFunc finalizer); void SetGetter(ClassIndexFunc getter); void SetMethod(const NzString& name, ClassFunc method); void SetSetter(ClassIndexFunc setter); + void SetStaticGetter(StaticIndexFunc getter); + void SetStaticMethod(const NzString& name, StaticFunc func); + void SetStaticSetter(StaticIndexFunc getter); private: static int ConstructorProxy(lua_State* state); @@ -44,18 +51,26 @@ class NzLuaClass 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); struct ClassInfo { std::vector methods; + std::vector staticMethods; ClassIndexFunc getter = nullptr; ClassIndexFunc setter = nullptr; ConstructorFunc constructor = nullptr; FinalizerFunc finalizer = nullptr; + StaticIndexFunc staticGetter = nullptr; + StaticIndexFunc staticSetter = nullptr; NzString name; + int globalTableRef = -1; }; 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 88a4984ee..ac79d2468 100644 --- a/include/Nazara/Lua/LuaClass.inl +++ b/include/Nazara/Lua/LuaClass.inl @@ -3,6 +3,7 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include #include template @@ -52,7 +53,8 @@ void NzLuaClass::Register(NzLuaInstance& lua) if (m_info->getter) { lua.PushValue(1); - lua.PushCFunction(GetterProxy, 1); + lua.PushValue(-2); + lua.PushCFunction(GetterProxy, 2); } else // Optimisation, plutôt que de rediriger vers une fonction C qui ne fera rien d'autre que rechercher @@ -69,33 +71,84 @@ void NzLuaClass::Register(NzLuaInstance& lua) lua.SetField("__newindex"); // Setter } - m_info->methods.resize(m_methods.size()); - - unsigned int index = 0; + m_info->methods.reserve(m_methods.size()); for (auto& pair : m_methods) { - m_info->methods[index] = pair.second; + m_info->methods.push_back(pair.second); lua.PushValue(1); - lua.PushInteger(index); + lua.PushInteger(m_info->methods.size() - 1); lua.PushCFunction(MethodProxy, 2); lua.SetField(pair.first); // Méthode - - index++; } } lua.Pop(); // On pop la metatable - if (m_info->constructor) + if (m_info->constructor || m_info->staticGetter || m_info->staticSetter || !m_info->staticMethods.empty()) { - lua.PushValue(1); - lua.PushCFunction(ConstructorProxy, 1); - lua.SetGlobal(m_info->name); + // Création de l'instance globale + lua.PushTable(); // Class = {} + + // Création de la metatable associée à la table globale + lua.PushTable(); // ClassMeta = {} + + if (m_info->constructor) + { + lua.PushValue(1); // ClassInfo + lua.PushCFunction(ConstructorProxy, 1); + lua.SetField("__call"); // ClassMeta.__call = ConstructorProxy + } + + if (m_info->staticGetter) + { + lua.PushValue(1); + lua.PushValue(-2); + 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.SetField("__index"); // ClassMeta.__index = StaticGetterProxy/ClassMeta + + if (m_info->staticSetter) + { + lua.PushValue(1); + lua.PushCFunction(StaticSetterProxy, 1); + lua.SetField("__newindex"); // ClassMeta.__newindex = StaticSetterProxy + } + + m_info->staticMethods.reserve(m_staticMethods.size()); + for (auto& pair : m_staticMethods) + { + m_info->staticMethods.push_back(pair.second); + + lua.PushValue(1); + lua.PushInteger(m_info->staticMethods.size() - 1); + + lua.PushCFunction(StaticMethodProxy, 2); + lua.SetField(pair.first); // ClassMeta.method = StaticMethodProxy + } + + lua.SetMetatable(-2); // setmetatable(Class, ClassMeta) + + lua.PushValue(-1); // Copie + lua.SetGlobal(m_info->name); // Class + + m_info->globalTableRef = lua.CreateReference(); } lua.Pop(); // On pop l'Userdata (contenant nos informations) } +template +void NzLuaClass::PushGlobalTable(NzLuaInstance& lua) +{ + lua.PushReference(m_info->globalTableRef); +} + template void NzLuaClass::SetConstructor(ConstructorFunc constructor) { @@ -126,6 +179,24 @@ void NzLuaClass::SetSetter(ClassIndexFunc setter) m_info->setter = setter; } +template +void NzLuaClass::SetStaticGetter(StaticIndexFunc getter) +{ + m_info->staticGetter = getter; +} + +template +void NzLuaClass::SetStaticMethod(const NzString& name, StaticFunc method) +{ + m_staticMethods[name] = method; +} + +template +void NzLuaClass::SetStaticSetter(StaticIndexFunc setter) +{ + m_info->staticSetter = setter; +} + template int NzLuaClass::ConstructorProxy(lua_State* state) { @@ -134,6 +205,8 @@ int NzLuaClass::ConstructorProxy(lua_State* state) ClassInfo* info = *static_cast(lua.ToUserdata(lua.GetIndexOfUpValue(1))); ConstructorFunc constructor = info->constructor; + lua.Remove(1); // On enlève l'argument "table" du stack + T* instance = constructor(lua); if (!instance) { @@ -169,10 +242,11 @@ int NzLuaClass::InfoDestructor(lua_State* state) { NzLuaInstance& lua = *NzLuaInstance::GetInstance(state); - std::shared_ptr* infoPtr = static_cast*>(lua.ToUserdata(lua.GetIndexOfUpValue(1))); + std::shared_ptr& infoPtr = *static_cast*>(lua.ToUserdata(lua.GetIndexOfUpValue(1))); + lua.DestroyReference(infoPtr->globalTableRef); using namespace std; // Obligatoire pour le destructeur - infoPtr->~shared_ptr(); // Si vous voyez une autre façon de faire, je suis preneur + infoPtr.~shared_ptr(); // Si vous voyez une autre façon de faire, je suis preneur return 0; } @@ -190,7 +264,7 @@ int NzLuaClass::GetterProxy(lua_State* state) if (!getter(lua, instance)) { // On accède alors à la table - lua.PushMetatable(info->name); + lua.PushValue(lua.GetIndexOfUpValue(2)); lua.PushValue(-2); lua.GetTable(); } @@ -209,6 +283,8 @@ int NzLuaClass::MethodProxy(lua_State* state) T& instance = *(*static_cast(lua.CheckUserdata(1, info->name))); + lua.Remove(1); // On enlève l'argument "userdata" du stack + return method(lua, instance); } @@ -233,4 +309,54 @@ int NzLuaClass::SetterProxy(lua_State* state) return 1; } +template +int NzLuaClass::StaticGetterProxy(lua_State* state) +{ + NzLuaInstance& lua = *NzLuaInstance::GetInstance(state); + + ClassInfo* info = *static_cast(lua.ToUserdata(lua.GetIndexOfUpValue(1))); + StaticIndexFunc getter = info->staticGetter; + + if (!getter(lua)) + { + // On accède alors à la table + lua.PushValue(lua.GetIndexOfUpValue(2)); + lua.PushValue(-2); + lua.GetTable(); + } + + return 1; +} + +template +int NzLuaClass::StaticMethodProxy(lua_State* state) +{ + NzLuaInstance& lua = *NzLuaInstance::GetInstance(state); + + ClassInfo* info = *static_cast(lua.ToUserdata(lua.GetIndexOfUpValue(1))); + int index = lua.ToInteger(lua.GetIndexOfUpValue(2)); + StaticFunc method = info->staticMethods[index]; + + return method(lua); +} + +template +int NzLuaClass::StaticSetterProxy(lua_State* state) +{ + NzLuaInstance& lua = *NzLuaInstance::GetInstance(state); + + ClassInfo* info = *static_cast(lua.ToUserdata(lua.GetIndexOfUpValue(1))); + StaticIndexFunc setter = info->staticSetter; + + if (!setter(lua)) + { + std::size_t length; + const char* str = lua.ToString(2, &length); + + lua.Error("Class \"" + info->name + "\" has no static field \"" + NzString(str, length) + ')'); + } + + return 1; +} + #include