// Copyright (C) 2015 Jérôme Leclercq // This file is part of the "Nazara Engine - Lua scripting module" // For conditions of distribution and use, see copyright notice in Config.hpp #include #include #include namespace Nz { template LuaClass::LuaClass(const String& name) : m_info(new ClassInfo) { m_info->name = name; } /* template void LuaClass::Inherit(LuaClass

& parent) { static_assert(std::is_base_of::value, "P must be a base of T"); m_info->parentInfo = parent.m_info; } */ template void LuaClass::Register(LuaInstance& lua) { // Le ClassInfo doit rester en vie jusqu'à la fin du script // Obliger l'instance de LuaClass à rester en vie dans cette fin serait contraignant pour l'utilisateur // J'utilise donc une astuce, la stocker dans une UserData associée avec chaque fonction de la metatable du type, // cette UserData disposera d'un finalizer qui libérera le ClassInfo // Ainsi c'est Lua qui va s'occuper de la destruction pour nous :-) // De même, l'utilisation d'un shared_ptr permet de garder la structure en vie même si l'instance est libérée avant le LuaClass void* info = lua.PushUserdata(sizeof(std::shared_ptr)); PlacementNew>(info, m_info); // On créé la table qui contiendra une méthode (Le finalizer) pour libérer le ClassInfo lua.PushTable(0, 1); lua.PushLightUserdata(info); lua.PushCFunction(InfoDestructor, 1); lua.SetField("__gc"); lua.SetMetatable(-2); // La table devient la metatable de l'UserData // Maintenant, nous allons associer l'UserData avec chaque fonction, de sorte qu'il reste en vie // aussi longtemps que nécessaire, et que le pointeur soit transmis à chaque méthode 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 if (m_info->getter) { lua.PushValue(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 // 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"); // Getter if (m_info->setter) { lua.PushValue(1); lua.PushCFunction(SetterProxy, 1); lua.SetField("__newindex"); // Setter } m_info->methods.reserve(m_methods.size()); for (auto& pair : m_methods) { m_info->methods.push_back(pair.second); lua.PushValue(1); lua.PushInteger(m_info->methods.size() - 1); lua.PushCFunction(MethodProxy, 2); lua.SetField(pair.first); // Méthode } } lua.Pop(); // On pop la metatable if (m_info->constructor || m_info->staticGetter || m_info->staticSetter || !m_info->staticMethods.empty()) { // 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 LuaClass::PushGlobalTable(LuaInstance& lua) { lua.PushReference(m_info->globalTableRef); } template void LuaClass::SetConstructor(ConstructorFunc constructor) { m_info->constructor = constructor; } template void LuaClass::SetFinalizer(FinalizerFunc finalizer) { m_info->finalizer = finalizer; } template void LuaClass::SetGetter(ClassIndexFunc getter) { m_info->getter = getter; } template void LuaClass::SetMethod(const String& name, ClassFunc method) { m_methods[name] = method; } template template std::enable_if_t::value> LuaClass::SetMethod(const String& name, R(P::*func)(Args...), DefArgs... defArgs) { SetMethod(name, [func, defArgs...] (LuaInstance& instance, T& object) -> int { LuaImplMethodProxy::Impl handler(instance, object, defArgs...); handler.ProcessArgs(); return handler.Invoke(func); }); } template template std::enable_if_t::value> LuaClass::SetMethod(const String& name, R(P::*func)(Args...) const, DefArgs... defArgs) { SetMethod(name, [func, defArgs...] (LuaInstance& instance, T& object) -> int { LuaImplMethodProxy::Impl handler(instance, object, defArgs...); handler.ProcessArgs(); return handler.Invoke(func); }); } template void LuaClass::SetSetter(ClassIndexFunc setter) { m_info->setter = setter; } template void LuaClass::SetStaticGetter(StaticIndexFunc getter) { m_info->staticGetter = getter; } template void LuaClass::SetStaticMethod(const String& name, StaticFunc method) { m_staticMethods[name] = method; } template template void LuaClass::SetStaticMethod(const String& name, R(*func)(Args...), DefArgs... defArgs) { SetStaticMethod(name, [func, defArgs...] (LuaInstance& instance) -> int { LuaImplFunctionProxy::Impl handler(instance); handler.ProcessArgs(); return handler.Invoke(func); }); } template void LuaClass::SetStaticSetter(StaticIndexFunc setter) { m_info->staticSetter = setter; } template int LuaClass::ConstructorProxy(lua_State* state) { LuaInstance& lua = *LuaInstance::GetInstance(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) { lua.Error("Constructor failed"); return 0; // Normalement jamais exécuté (l'erreur provoquant une exception) } T** ud = static_cast(lua.PushUserdata(sizeof(T*))); *ud = instance; lua.SetMetatable(info->name); return 1; } template int LuaClass::FinalizerProxy(lua_State* state) { LuaInstance& lua = *LuaInstance::GetInstance(state); ClassInfo* info = *static_cast(lua.ToUserdata(lua.GetIndexOfUpValue(1))); FinalizerFunc finalizer = info->finalizer; T* instance = *static_cast(lua.CheckUserdata(1, info->name)); if (!finalizer || finalizer(lua, *instance)) delete instance; return 0; } template int LuaClass::InfoDestructor(lua_State* state) { LuaInstance& lua = *LuaInstance::GetInstance(state); 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 return 0; } 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; T& instance = *(*static_cast(lua.CheckUserdata(1, info->name))); if (!getter(lua, instance)) { // On accède alors à la table lua.PushValue(lua.GetIndexOfUpValue(2)); lua.PushValue(-2); lua.GetTable(); } return 1; } template int LuaClass::MethodProxy(lua_State* state) { 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]; T& instance = *(*static_cast(lua.CheckUserdata(1, info->name))); lua.Remove(1); // On enlève l'argument "userdata" du stack return method(lua, instance); } template int LuaClass::SetterProxy(lua_State* state) { LuaInstance& lua = *LuaInstance::GetInstance(state); ClassInfo* info = *static_cast(lua.ToUserdata(lua.GetIndexOfUpValue(1))); ClassIndexFunc setter = info->setter; T& instance = *(*static_cast(lua.CheckUserdata(1, info->name))); if (!setter(lua, instance)) { std::size_t length; const char* str = lua.ToString(2, &length); lua.Error("Class \"" + info->name + "\" has no field \"" + String(str, length) + ')'); } return 1; } template int LuaClass::StaticGetterProxy(lua_State* state) { LuaInstance& lua = *LuaInstance::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 LuaClass::StaticMethodProxy(lua_State* state) { LuaInstance& lua = *LuaInstance::GetInstance(state); ClassInfo* info = *static_cast(lua.ToUserdata(lua.GetIndexOfUpValue(1))); unsigned int index = static_cast(lua.ToInteger(lua.GetIndexOfUpValue(2))); StaticFunc method = info->staticMethods[index]; return method(lua); } template int LuaClass::StaticSetterProxy(lua_State* state) { LuaInstance& lua = *LuaInstance::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 \"" + String(str, length) + ')'); } return 1; } } #include