// Copyright (C) 2013 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 template NzLuaClass::NzLuaClass(const NzString& name) : m_info(new ClassInfo) { m_info->name = name; } /* template void NzLuaClass::Inherit(NzLuaClass

& parent) { static_assert(std::is_base_of::value, "P must be a base of T"); m_info->parentInfo = parent.m_info; } */ template void NzLuaClass::Register(NzLuaInstance& 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 :-) // PS: 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)); new (info) std::shared_ptr(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.PushCFunction(GetterProxy, 1); } 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.resize(m_methods.size()); unsigned int index = 0; for (auto& pair : m_methods) { m_info->methods[index] = pair.second; lua.PushValue(1); lua.PushInteger(index); lua.PushCFunction(MethodProxy, 2); lua.SetField(pair.first); // Méthode index++; } } lua.Pop(); if (m_info->constructor) { lua.PushValue(1); lua.PushCFunction(ConstructorProxy, 1); lua.SetGlobal(m_info->name); } } template void NzLuaClass::SetConstructor(ConstructorFunc constructor) { m_info->constructor = constructor; } template void NzLuaClass::SetFinalizer(FinalizerFunc finalizer) { m_info->finalizer = finalizer; } template void NzLuaClass::SetGetter(ClassIndexFunc getter) { m_info->getter = getter; } template void NzLuaClass::SetMethod(const NzString& name, ClassFunc method) { m_methods[name] = method; } template void NzLuaClass::SetSetter(ClassIndexFunc setter) { m_info->setter = setter; } template int NzLuaClass::ConstructorProxy(lua_State* state) { NzLuaInstance& lua = *NzLuaInstance::GetInstance(state); ClassInfo* info = *static_cast(lua.ToUserdata(lua.GetIndexOfUpValue(1))); ConstructorFunc constructor = info->constructor; T* instance = constructor(lua); if (!instance) { lua.Error("Constructor failed"); return 0; // Normalement pas nécessaire } T** ud = static_cast(lua.PushUserdata(sizeof(T*))); *ud = instance; lua.SetMetatable(info->name); return 1; } template int NzLuaClass::FinalizerProxy(lua_State* state) { NzLuaInstance& lua = *NzLuaInstance::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 NzLuaClass::InfoDestructor(lua_State* state) { NzLuaInstance& lua = *NzLuaInstance::GetInstance(state); std::shared_ptr* infoPtr = static_cast*>(lua.ToUserdata(lua.GetIndexOfUpValue(1))); 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 NzLuaClass::GetterProxy(lua_State* state) { NzLuaInstance& lua = *NzLuaInstance::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.PushMetatable(info->name); lua.PushValue(-2); lua.GetTable(); } return 1; } template int NzLuaClass::MethodProxy(lua_State* state) { NzLuaInstance& lua = *NzLuaInstance::GetInstance(state); ClassInfo* info = *static_cast(lua.ToUserdata(lua.GetIndexOfUpValue(1))); int index = lua.ToInteger(lua.GetIndexOfUpValue(2)); ClassFunc method = info->methods[index]; T& instance = *(*static_cast(lua.CheckUserdata(1, info->name))); return method(lua, instance); } template int NzLuaClass::SetterProxy(lua_State* state) { NzLuaInstance& lua = *NzLuaInstance::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)) lua.Error("Field not found"); return 1; } #include