#ifndef ANKI_SCRIPT_LUA_BINDER_H #define ANKI_SCRIPT_LUA_BINDER_H #include "anki/util/Assert.h" #include "anki/util/StdTypes.h" #include #ifndef ANKI_LUA_HPP # error "See file" #endif namespace anki { /// Internal lua stuff namespace lua_detail { //============================================================================== /// lua userdata struct UserData { void* ptr = nullptr; Bool8 gc = false; ///< Garbage collection on? }; //============================================================================== // Flags constexpr U32 LF_NONE = 0; constexpr U32 LF_TRANFER_OWNERSHIP = 1; //============================================================================== /// Class identification template struct ClassProxy { static const char* NAME; ///< Used to check the signature of the user data static const char* getName() { ANKI_ASSERT(NAME != nullptr && "Class already wrapped elsewhere"); return NAME; } }; template const char* ClassProxy::NAME = nullptr; //============================================================================== /// Make sure that the arguments match the argsCount number extern void checkArgsCount(lua_State* l, I argsCount); /// Create a new LUA class extern void createClass(lua_State* l, const char* className); /// Add new function in a class that it's already in the stack extern void pushCFunctionMethod(lua_State* l, const char* name, lua_CFunction luafunc); /// Add a new static function in the class extern void pushCFunctionStatic(lua_State* l, const char* className, const char* name, lua_CFunction luafunc); //============================================================================== /// Used mainly to push a method's return value to the stack template struct PushStack { void operator()(lua_State* l, Class& x) { UserData* d = (UserData*)lua_newuserdata(l, sizeof(UserData)); luaL_setmetatable(l, ClassProxy::getName()); d->ptr = new Class(x); d->gc = true; } }; // Specialization ref template struct PushStack { void operator()(lua_State* l, Class& x) { UserData* d = (UserData*)lua_newuserdata(l, sizeof(UserData)); luaL_setmetatable(l, ClassProxy::getName()); d->ptr = &x; d->gc = flags & LF_TRANFER_OWNERSHIP; } }; // Specialization const ref template struct PushStack { void operator()(lua_State* l, const Class& x) { UserData* d = (UserData*)lua_newuserdata(l, sizeof(UserData)); luaL_setmetatable(l, ClassProxy::getName()); d->ptr = &const_cast(x); d->gc = flags & LF_TRANFER_OWNERSHIP; } }; // Specialization ptr template struct PushStack { void operator()(lua_State* l, Class* x) { UserData* d = (UserData*)lua_newuserdata(l, sizeof(UserData)); luaL_setmetatable(l, ClassProxy::getName()); d->ptr = x; d->gc = flags & LF_TRANFER_OWNERSHIP; } }; // Specialization const ptr template struct PushStack { void operator()(lua_State* l, Class* x) { UserData* d = (UserData*)lua_newuserdata(l, sizeof(UserData)); luaL_setmetatable(l, ClassProxy::getName()); d->ptr = const_cast(x); d->gc = flags & LF_TRANFER_OWNERSHIP; } }; // Specialization const char* template struct PushStack { void operator()(lua_State* l, const char* x) { lua_pushstring(l, x); } }; #define ANKI_PUSH_STACK_TEMPLATE_SPECIALIZATION(Type_, luafunc_) \ template \ struct PushStack \ { \ void operator()(lua_State* l, Type_ x) \ { \ luafunc_(l, x); \ } \ }; ANKI_PUSH_STACK_TEMPLATE_SPECIALIZATION(I8, lua_pushnumber) ANKI_PUSH_STACK_TEMPLATE_SPECIALIZATION(I16, lua_pushnumber) ANKI_PUSH_STACK_TEMPLATE_SPECIALIZATION(I32, lua_pushnumber) ANKI_PUSH_STACK_TEMPLATE_SPECIALIZATION(I64, lua_pushnumber) ANKI_PUSH_STACK_TEMPLATE_SPECIALIZATION(U8, lua_pushnumber) ANKI_PUSH_STACK_TEMPLATE_SPECIALIZATION(U16, lua_pushnumber) ANKI_PUSH_STACK_TEMPLATE_SPECIALIZATION(U32, lua_pushnumber) ANKI_PUSH_STACK_TEMPLATE_SPECIALIZATION(U64, lua_pushnumber) ANKI_PUSH_STACK_TEMPLATE_SPECIALIZATION(F64, lua_pushnumber) ANKI_PUSH_STACK_TEMPLATE_SPECIALIZATION(F32, lua_pushnumber) ANKI_PUSH_STACK_TEMPLATE_SPECIALIZATION(Bool, lua_pushnumber) //============================================================================== /// Used to get the function arguments from the stack template struct StackGet { Class operator()(lua_State* l) { UserData* udata = (UserData*)luaL_checkudata(l, stackIndex, ClassProxy::getName()); const Class* a = reinterpret_cast(udata->ptr); return Class(*a); } }; // Specialization const ref template struct StackGet { const Class& operator()(lua_State* l) { UserData* udata = (UserData*)luaL_checkudata(l, stackIndex, ClassProxy::getName()); const Class* a = reinterpret_cast(udata->ptr); return *a; } }; // Specialization ref template struct StackGet { Class& operator()(lua_State* l) { UserData* udata = (UserData*)luaL_checkudata(l, stackIndex, ClassProxy::getName()); Class* a = reinterpret_cast(udata->ptr); return *a; } }; // Specialization const ptr template struct StackGet { const Class* operator()(lua_State* l) { UserData* udata = (UserData*)luaL_checkudata(l, stackIndex, ClassProxy::getName()); const Class* a = reinterpret_cast(udata->ptr); return a; } }; // Specialization ptr template struct StackGet { Class* operator()(lua_State* l) { UserData* udata = (UserData*)luaL_checkudata(l, stackIndex, ClassProxy::getName()); Class* a = reinterpret_cast(udata->ptr); return a; } }; #define ANKI_STACK_GET_TEMPLATE_SPECIALIZATION(Type_, luafunc_) \ template \ struct StackGet \ { \ Type_ operator()(lua_State* l) \ { \ return luafunc_(l, stackIndex); \ } \ }; ANKI_STACK_GET_TEMPLATE_SPECIALIZATION(F32, luaL_checknumber) ANKI_STACK_GET_TEMPLATE_SPECIALIZATION(F64, luaL_checknumber) ANKI_STACK_GET_TEMPLATE_SPECIALIZATION(I8, luaL_checkinteger) ANKI_STACK_GET_TEMPLATE_SPECIALIZATION(I16, luaL_checkinteger) ANKI_STACK_GET_TEMPLATE_SPECIALIZATION(I32, luaL_checkinteger) ANKI_STACK_GET_TEMPLATE_SPECIALIZATION(I64, luaL_checkinteger) ANKI_STACK_GET_TEMPLATE_SPECIALIZATION(U8, luaL_checkunsigned) ANKI_STACK_GET_TEMPLATE_SPECIALIZATION(U16, luaL_checkunsigned) ANKI_STACK_GET_TEMPLATE_SPECIALIZATION(U32, luaL_checkunsigned) ANKI_STACK_GET_TEMPLATE_SPECIALIZATION(U64, luaL_checkunsigned) ANKI_STACK_GET_TEMPLATE_SPECIALIZATION(const char*, luaL_checkstring) ANKI_STACK_GET_TEMPLATE_SPECIALIZATION(Bool, luaL_checkunsigned) //============================================================================== /// Call a function template struct CallFunction; // R (_1) template struct CallFunction { int operator()(lua_State* l, TReturn (*func)(Arg0)) { TReturn out = (*func)(StackGet()(l)); PushStack ps; ps(l, out); return 1; } }; // R (_1, _2) template struct CallFunction { int operator()(lua_State* l, TReturn (*func)(Arg0, Arg1)) { TReturn out = (*func)(StackGet()(l), StackGet()(l)); PushStack ps; ps(l, out); return 1; } }; // R (_1, _2, _3) template struct CallFunction { int operator()(lua_State* l, TReturn (*func)(Arg0, Arg1, Arg2)) { TReturn out = (*func)(StackGet()(l), StackGet()(l), StackGet()(l)); PushStack ps; ps(l, out); return 1; } }; // void (_1) template struct CallFunction { int operator()(lua_State* l, void (*func)(Arg0)) { (*func)(StackGet()(l)); return 0; } }; // void (_1, _2) template struct CallFunction { int operator()(lua_State* l, void (*func)(Arg0, Arg1)) { (*func)(StackGet()(l), StackGet()(l)); return 0; } }; // void (_1, _2, _3) template struct CallFunction { int operator()(lua_State* l, void (*func)(Arg0, Arg1, Arg2)) { (*func)(StackGet()(l), StackGet()(l), StackGet()(l)); return 0; } }; // R (void) template struct CallFunction { int operator()(lua_State* l, TReturn (*func)(void)) { TReturn out = (*func)(); PushStack ps; ps(l, out); return 1; } }; // void (void) template struct CallFunction { int operator()(lua_State* /*l*/, void (*func)(void)) { (*func)(); return 0; } }; //============================================================================== /// Call constructor template struct CallConstructor; // none template struct CallConstructor { Class* operator()(lua_State*) { return new Class(); } }; // _1 template struct CallConstructor { Class* operator()(lua_State* l) { return new Class(StackGet()(l)); } }; // _1, _2 template struct CallConstructor { Class* operator()(lua_State* l) { return new Class(StackGet()(l), StackGet()(l)); } }; // _1, _2, _3 template struct CallConstructor { Class* operator()(lua_State* l) { return new Class(StackGet()(l), StackGet()(l), StackGet()(l)); } }; // _1, _2, _3, _4 template struct CallConstructor { Class* operator()(lua_State* l) { return new Class(StackGet()(l), StackGet()(l), StackGet()(l), StackGet()(l)); } }; //============================================================================== /// Make a method function. Used like this: /// @code /// Foo foo; // An instance /// MethodFunctionalizer::In::func(&foo, 123); /// // Equivelent of: /// foo.bar(123); /// @endcode template struct MethodFunctionalizer; template struct MethodFunctionalizer { template struct In { static TReturn func(Class* self, Args... args) { return (self->*method)(args...); } }; }; template struct MethodFunctionalizer { template struct In { static TReturn func(const Class* self, Args... args) { return (self->*method)(args...); } }; }; //============================================================================== /// Signature for constructor template struct ConstructorSignature { static int luafunc(lua_State* l) { checkArgsCount(l, sizeof...(Args)); UserData* d = (UserData*)lua_newuserdata(l, sizeof(UserData)); luaL_setmetatable(l, ClassProxy::getName()); d->ptr = CallConstructor()(l); d->gc = true; return 1; } }; //============================================================================== /// Destructor signature template struct DestructorSignature { static int luafunc(lua_State* l) { checkArgsCount(l, 1); UserData* d = (UserData*)luaL_checkudata(l, 1, ClassProxy::getName()); if(d->gc) { Class* obj = reinterpret_cast(d->ptr); delete obj; } return 0; } }; //============================================================================== /// Function signature template struct FunctionSignature; template struct FunctionSignature { template static int luafunc(lua_State* l) { checkArgsCount(l, sizeof...(Args)); auto ff = func; // A hack that saves GCC CallFunction cf; return cf(l, func); } }; } // end namespace lua_detail //============================================================================== // Macros /// Don't use it directly #define ANKI_LUA_DESTRUCTOR() \ lua_detail::pushCFunctionMethod(l_, "__gc", \ &lua_detail::DestructorSignature::luafunc); /// Start wrapping a class. Don't add a destructor (if for example the class /// has a private derstructor) #define ANKI_LUA_CLASS_BEGIN_NO_DESTRUCTOR(luaBinder_, Class_) { \ typedef Class_ Class; \ lua_State* l_ = luaBinder_._getLuaState(); \ lua_detail::ClassProxy::NAME = #Class_; \ lua_detail::createClass(l_, lua_detail::ClassProxy::getName()); /// Start wrapping a class #define ANKI_LUA_CLASS_BEGIN(luaBinder_, Class_) \ ANKI_LUA_CLASS_BEGIN_NO_DESTRUCTOR(luaBinder_, Class_) \ ANKI_LUA_DESTRUCTOR() /// End wrapping a class #define ANKI_LUA_CLASS_END() lua_settop(l_, 0); } /// Define a constructor. Call it from lua @code a = Foo.new(...) @endcode. #define ANKI_LUA_CONSTRUCTOR(...) \ lua_detail::pushCFunctionStatic(l_, \ lua_detail::ClassProxy::getName(), "new", \ &lua_detail::ConstructorSignature::luafunc); /// Define a static method with flags #define ANKI_LUA_STATIC_METHOD_FLAGS(name_, smethodPtr_, flags_) \ lua_detail::pushCFunctionStatic(l_, \ lua_detail::ClassProxy::getName(), name_, \ &lua_detail::FunctionSignature::luafunc); /// Define a static method no flags #define ANKI_LUA_STATIC_METHOD(name_, smethodPtr_) \ ANKI_LUA_STATIC_METHOD_FLAGS(name_, smethodPtr_, lua_detail::LF_NONE) /// Define a function as method with flags #define ANKI_LUA_FUNCTION_AS_METHOD_FLAGS(name_, funcPtr_, flags_) \ lua_detail::pushCFunctionMethod(l_, name_, \ &lua_detail::FunctionSignature::luafunc); /// Define a function as method no flags #define ANKI_LUA_FUNCTION_AS_METHOD(name_, funcPtr_) \ ANKI_LUA_FUNCTION_AS_METHOD_FLAGS(name_, funcPtr_, lua_detail::LF_NONE) /// Define a method with flags #define ANKI_LUA_METHOD_FLAGS(name_, methodPtr_, flags_) \ ANKI_LUA_FUNCTION_AS_METHOD_FLAGS(name_, \ &lua_detail::MethodFunctionalizer< \ decltype(methodPtr_)>::In::func, flags_) /// Define a method no flags #define ANKI_LUA_METHOD(name_, methodPtr_) \ ANKI_LUA_METHOD_FLAGS(name_, methodPtr_, lua_detail::LF_NONE) //============================================================================== /// Lua binder class. A wrapper on top of LUA class LuaBinder { public: LuaBinder(); ~LuaBinder(); /// @name Accessors /// { lua_State* _getLuaState() { return l; } /// } /// Expose a variable to the lua state template void exposeVariable(const char* name, T* y) { using namespace lua_detail; UserData* d = (UserData*)lua_newuserdata(l, sizeof(UserData)); d->ptr = y; d->gc = false; luaL_setmetatable(l, ClassProxy::getName()); lua_setglobal(l, name); } /// Evaluate a file void evalFile(const char* filename); /// Evaluate a string void evalString(const char* str); /// For debugging purposes static void stackDump(lua_State* l); private: lua_State* l = nullptr; }; } // end namespace anki #endif