Lua/LuaClass: Add inheritance (first implementation)
Former-commit-id: 6c7f8d7dace5c857ae71958e15cc13d6a6f9ccb2
This commit is contained in:
parent
3cdf6a9f44
commit
83bd028e8f
|
|
@ -13,14 +13,16 @@
|
|||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
//#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
template<class T/*, class P = void*/>
|
||||
template<class T>
|
||||
class LuaClass
|
||||
{
|
||||
//static_assert(std::is_same<P, void>::value || std::is_base_of<P, T>::value, "P must be a base of T");
|
||||
template<class U>
|
||||
friend class LuaClass;
|
||||
|
||||
public:
|
||||
using ClassFunc = std::function<int(LuaInstance& lua, T& instance)>;
|
||||
|
|
@ -32,7 +34,8 @@ namespace Nz
|
|||
|
||||
LuaClass(const String& name);
|
||||
|
||||
//void Inherit(LuaClass<P>& parent);
|
||||
template<class P>
|
||||
void Inherit(LuaClass<P>& parent);
|
||||
|
||||
void Register(LuaInstance& lua);
|
||||
|
||||
|
|
@ -44,7 +47,6 @@ namespace Nz
|
|||
void SetMethod(const String& name, ClassFunc method);
|
||||
template<typename R, typename P, typename... Args, typename... DefArgs> std::enable_if_t<std::is_base_of<P, T>::value> SetMethod(const String& name, R(P::*func)(Args...), DefArgs... defArgs);
|
||||
template<typename R, typename P, typename... Args, typename... DefArgs> std::enable_if_t<std::is_base_of<P, 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<void(LuaInstance& lua, T& instance)>;
|
||||
using InstanceGetter = std::function<T*(LuaInstance& lua)>;
|
||||
|
||||
struct ClassInfo
|
||||
{
|
||||
std::vector<ClassFunc> methods;
|
||||
std::vector<ParentFunc> parentGetters;
|
||||
std::vector<StaticFunc> staticMethods;
|
||||
std::unordered_map<String, InstanceGetter> 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<ClassInfo>& 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<String, ClassFunc> m_methods;
|
||||
std::map<String, StaticFunc> m_staticMethods;
|
||||
std::shared_ptr<ClassInfo> m_info;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include <Nazara/Core/Error.hpp>
|
||||
#include <Nazara/Core/MemoryHelper.hpp>
|
||||
#include <type_traits>
|
||||
#include <Nazara/Lua/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
|
|
@ -23,6 +24,26 @@ namespace Nz
|
|||
m_info->parentInfo = parent.m_info;
|
||||
}
|
||||
*/
|
||||
|
||||
template<class T>
|
||||
template<class P>
|
||||
inline void LuaClass<T>::Inherit(LuaClass<P>& parent)
|
||||
{
|
||||
static_assert(!std::is_same<P, T>::value || std::is_base_of<P, T>::value, "P must be a base of T");
|
||||
|
||||
std::shared_ptr<typename LuaClass<P>::ClassInfo>& parentInfo = parent.m_info;
|
||||
|
||||
parentInfo->instanceGetters[m_info->name] = [info = m_info](LuaInstance& lua) -> P*
|
||||
{
|
||||
return *static_cast<T**>(lua.CheckUserdata(1, info->name));
|
||||
};
|
||||
|
||||
m_info->parentGetters.emplace_back([parentInfo] (LuaInstance& lua, T& instance)
|
||||
{
|
||||
LuaClass<P>::Get(parentInfo, lua, instance);
|
||||
});
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void LuaClass<T>::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.PushValue(-2);
|
||||
lua.PushCFunction(FinalizerProxy, 1);
|
||||
lua.SetField("__gc");
|
||||
|
||||
if (m_info->getter || !m_info->parentGetters.empty())
|
||||
{
|
||||
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<T**>(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<T, Args...>::Impl<DefArgs...> handler(instance, object, defArgs...);
|
||||
typename LuaImplMethodProxy<T, Args...>::template Impl<DefArgs...> 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<ClassInfo**>(lua.ToUserdata(lua.GetIndexOfUpValue(1)));
|
||||
ConstructorFunc constructor = info->constructor;
|
||||
std::shared_ptr<ClassInfo>& info = *static_cast<std::shared_ptr<ClassInfo>*>(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<ClassInfo**>(lua.ToUserdata(lua.GetIndexOfUpValue(1)));
|
||||
FinalizerFunc finalizer = info->finalizer;
|
||||
std::shared_ptr<ClassInfo>& info = *static_cast<std::shared_ptr<ClassInfo>*>(lua.ToUserdata(lua.GetIndexOfUpValue(1)));
|
||||
const FinalizerFunc& finalizer = info->finalizer;
|
||||
|
||||
T* instance = *static_cast<T**>(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<ClassInfo>& infoPtr = *static_cast<std::shared_ptr<ClassInfo>*>(lua.ToUserdata(lua.GetIndexOfUpValue(1)));
|
||||
lua.DestroyReference(infoPtr->globalTableRef);
|
||||
std::shared_ptr<ClassInfo>& info = *static_cast<std::shared_ptr<ClassInfo>*>(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<class T>
|
||||
void LuaClass<T>::Get(const std::shared_ptr<ClassInfo>& 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<class T>
|
||||
int LuaClass<T>::GetterProxy(lua_State* state)
|
||||
{
|
||||
LuaInstance& lua = *LuaInstance::GetInstance(state);
|
||||
|
||||
ClassInfo* info = *static_cast<ClassInfo**>(lua.ToUserdata(lua.GetIndexOfUpValue(1)));
|
||||
ClassIndexFunc getter = info->getter;
|
||||
std::shared_ptr<ClassInfo>& info = *static_cast<std::shared_ptr<ClassInfo>*>(lua.ToUserdata(lua.GetIndexOfUpValue(1)));
|
||||
|
||||
T& instance = *(*static_cast<T**>(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<ClassInfo**>(lua.ToUserdata(lua.GetIndexOfUpValue(1)));
|
||||
unsigned int index = static_cast<unsigned int>(lua.ToInteger(lua.GetIndexOfUpValue(2)));
|
||||
ClassFunc method = info->methods[index];
|
||||
std::shared_ptr<ClassInfo>& info = *static_cast<std::shared_ptr<ClassInfo>*>(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<T**>(lua.CheckUserdata(1, info->name)));
|
||||
lua.Remove(1); //< Remove the instance from the Lua stack
|
||||
|
||||
return method(lua, instance);
|
||||
unsigned int index = static_cast<unsigned int>(lua.ToInteger(lua.GetIndexOfUpValue(2)));
|
||||
const ClassFunc& method = info->methods[index];
|
||||
return method(lua, *instance);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
|
|
@ -335,8 +400,8 @@ namespace Nz
|
|||
{
|
||||
LuaInstance& lua = *LuaInstance::GetInstance(state);
|
||||
|
||||
ClassInfo* info = *static_cast<ClassInfo**>(lua.ToUserdata(lua.GetIndexOfUpValue(1)));
|
||||
ClassIndexFunc setter = info->setter;
|
||||
std::shared_ptr<ClassInfo>& info = *static_cast<std::shared_ptr<ClassInfo>*>(lua.ToUserdata(lua.GetIndexOfUpValue(1)));
|
||||
const ClassIndexFunc& setter = info->setter;
|
||||
|
||||
T& instance = *(*static_cast<T**>(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<ClassInfo**>(lua.ToUserdata(lua.GetIndexOfUpValue(1)));
|
||||
StaticIndexFunc getter = info->staticGetter;
|
||||
std::shared_ptr<ClassInfo>& info = *static_cast<std::shared_ptr<ClassInfo>*>(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<ClassInfo**>(lua.ToUserdata(lua.GetIndexOfUpValue(1)));
|
||||
std::shared_ptr<ClassInfo>& info = *static_cast<std::shared_ptr<ClassInfo>*>(lua.ToUserdata(lua.GetIndexOfUpValue(1)));
|
||||
unsigned int index = static_cast<unsigned int>(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<ClassInfo**>(lua.ToUserdata(lua.GetIndexOfUpValue(1)));
|
||||
StaticIndexFunc setter = info->staticSetter;
|
||||
std::shared_ptr<ClassInfo>& info = *static_cast<std::shared_ptr<ClassInfo>*>(lua.ToUserdata(lua.GetIndexOfUpValue(1)));
|
||||
const StaticIndexFunc& setter = info->staticSetter;
|
||||
|
||||
if (!setter(lua))
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue