Lua/LuaClass: Add inheritance (first implementation)

Former-commit-id: 6c7f8d7dace5c857ae71958e15cc13d6a6f9ccb2
This commit is contained in:
Lynix 2015-12-13 03:29:57 +01:00
parent 3cdf6a9f44
commit 83bd028e8f
2 changed files with 135 additions and 62 deletions

View File

@ -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;

View File

@ -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.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<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))
{