#ifndef _LUNAR_H #define _LUNAR_H #include #include #include "script.h" #include "luaincl.h" #include "script_method.h" template class Lunar { typedef struct { T *pT; } userdataType; public: typedef Executor* mfp; typedef struct { const char *name; mfp mfunc; } RegType; static void Register(Script* script, const std::string& className, const ScriptMethod* scriptMethods) { if(script != NULL) Register(script->getLuaState(), className, scriptMethods); } static void Register(lua_State *L, const std::string& className, const ScriptMethod* scriptMethods) { Lunar::className = className; Lunar::scriptMethods = scriptMethods; lua_newtable(L); int methods = lua_gettop(L); luaL_newmetatable(L, Lunar::className.c_str()); int metatable = lua_gettop(L); // store method table in globals so that // scripts can add functions written in Lua. lua_pushvalue(L, methods); set(L, LUA_GLOBALSINDEX, Lunar::className.c_str()); // hide metatable from Lua getmetatable() lua_pushvalue(L, methods); set(L, metatable, "__metatable"); lua_pushvalue(L, methods); set(L, metatable, "__index"); lua_pushcfunction(L, tostring_T); set(L, metatable, "__tostring"); lua_pushcfunction(L, gc_T); set(L, metatable, "__gc"); lua_newtable(L); // mt for method table lua_pushcfunction(L, new_T); lua_pushvalue(L, -1); // dup new_T function set(L, methods, "new"); // add new_T to method table set(L, -3, "__call"); // mt.__call = new_T lua_setmetatable(L, methods); // fill method table with methods from class T /* as it was originally for (RegType *l = T::methods; l->name; l++) { lua_pushstring(L, l->name); lua_pushlightuserdata(L, (void*)l); lua_pushcclosure(L, thunk, 1); lua_settable(L, methods); }*/ for (unsigned int i = 0; i < Lunar::scriptMethods->size(); i++) { lua_pushstring(L, Lunar::scriptMethods->name(i).c_str()); lua_pushlightuserdata(L, (void*)Lunar::scriptMethods->executor(i)); lua_pushcclosure(L, thunk, 1); lua_settable(L, methods); } lua_pop(L, 2); // drop metatable and method table } // call named lua method from userdata method table static int call(lua_State *L, const char *method, int nargs=0, int nresults=LUA_MULTRET, int errfunc=0) { int base = lua_gettop(L) - nargs; // userdata index if (!luaL_checkudata(L, base, Lunar::className.c_str())) { lua_settop(L, base-1); // drop userdata and args lua_pushfstring(L, "not a valid %s userdata", Lunar::className.c_str()); return -1; } lua_pushstring(L, method); // method name lua_gettable(L, base); // get method from userdata if (lua_isnil(L, -1)) { // no method? lua_settop(L, base-1); // drop userdata and args lua_pushfstring(L, "%s missing method '%s'", Lunar::className.c_str(), method); return -1; } lua_insert(L, base); // put method under userdata, args int status = lua_pcall(L, 1+nargs, nresults, errfunc); // call method if (status) { const char *msg = lua_tostring(L, -1); if (msg == NULL) msg = "(error with no message)"; lua_pushfstring(L, "%s:%s status = %d\n%s", Lunar::className.c_str(), method, status, msg); lua_remove(L, base); // remove old message return -1; } return lua_gettop(L) - base + 1; // number of results } // push onto the Lua stack a userdata containing a pointer to T object static int push(lua_State *L, T *obj, bool gc=false) { if (!obj) { lua_pushnil(L); return 0; } luaL_getmetatable(L, Lunar::className.c_str()); // lookup metatable in Lua registry if (lua_isnil(L, -1)) luaL_error(L, "%s missing metatable", Lunar::className.c_str()); int mt = lua_gettop(L); subtable(L, mt, "userdata", "v"); userdataType *ud = static_cast(pushuserdata(L, obj, sizeof(userdataType))); if (ud) { ud->pT = obj; // store pointer to object in userdata lua_pushvalue(L, mt); lua_setmetatable(L, -2); if (gc == false) { lua_checkstack(L, 3); subtable(L, mt, "do not trash", "k"); lua_pushvalue(L, -2); lua_pushboolean(L, 1); lua_settable(L, -3); lua_pop(L, 1); } } lua_replace(L, mt); lua_settop(L, mt); return mt; // index of userdata containing pointer to T object } static int insertObject(lua_State* L, T* obj, const std::string& name, bool gc=false) { int objectRef = push(L, obj, gc); lua_pushlstring(L, name.c_str(), name.size()); lua_pushvalue(L, objectRef ); lua_settable(L, LUA_GLOBALSINDEX ); return objectRef; } static int insertObject(Script* script, T* obj, const std::string& name, bool gc=false) { assert (script != NULL); return insertObject(script->getLuaState(), obj, name, gc); } // get userdata from Lua stack and return pointer to T object static T *check(lua_State *L, int narg) { userdataType *ud = static_cast(luaL_checkudata(L, narg, Lunar::className.c_str())); if(!ud) luaL_typerror(L, narg, Lunar::className.c_str()); return ud->pT; // pointer to T object } private: Lunar(); // hide default constructor // member function dispatcher static int thunk(lua_State *L) { // stack has userdata, followed by method args T *obj = check(L, 1); // get 'self', or if you prefer, 'this' lua_remove(L, 1); // remove self so member function args start at index 1 // get member function from upvalue mfp l = static_cast(lua_touserdata(L, lua_upvalueindex(1))); (*l)(obj, L); // call member function return l->hasRetVal()? 1 : 0; } // create a new T object and // push onto the Lua stack a userdata containing a pointer to T object static int new_T(lua_State *L) { lua_remove(L, 1); // use classname:new(), instead of classname.new() T *obj = new T(); // call constructor for T objects push(L, obj, true); // gc_T will delete this object return 1; // userdata containing pointer to T object } // garbage collection metamethod static int gc_T(lua_State *L) { if (luaL_getmetafield(L, 1, "do not trash")) { lua_pushvalue(L, 1); // dup userdata lua_gettable(L, -2); if (!lua_isnil(L, -1)) return 0; // do not delete object } userdataType *ud = static_cast(lua_touserdata(L, 1)); T *obj = ud->pT; if (obj) delete obj; // call destructor for T objects return 0; } static int tostring_T (lua_State *L) { char buff[32]; userdataType *ud = static_cast(lua_touserdata(L, 1)); T *obj = ud->pT; sprintf(buff, "%p", obj); lua_pushfstring(L, "%s (%s)", Lunar::className.c_str(), buff); return 1; } static void set(lua_State *L, int table_index, const char *key) { lua_pushstring(L, key); lua_insert(L, -2); // swap value and key lua_settable(L, table_index); } static void weaktable(lua_State *L, const char *mode) { lua_newtable(L); lua_pushvalue(L, -1); // table is its own metatable lua_setmetatable(L, -2); lua_pushliteral(L, "__mode"); lua_pushstring(L, mode); lua_settable(L, -3); // metatable.__mode = mode } static void subtable(lua_State *L, int tindex, const char *name, const char *mode) { lua_pushstring(L, name); lua_gettable(L, tindex); if (lua_isnil(L, -1)) { lua_pop(L, 1); lua_checkstack(L, 3); weaktable(L, mode); lua_pushstring(L, name); lua_pushvalue(L, -2); lua_settable(L, tindex); } } static void *pushuserdata(lua_State *L, void *key, size_t sz) { void *ud = 0; lua_pushlightuserdata(L, key); lua_gettable(L, -2); // lookup[key] if (lua_isnil(L, -1)) { lua_pop(L, 1); // drop nil lua_checkstack(L, 3); ud = lua_newuserdata(L, sz); // create new userdata lua_pushlightuserdata(L, key); lua_pushvalue(L, -2); // dup userdata lua_settable(L, -4); // lookup[key] = userdata } return ud; } private: static std::string className; static const ScriptMethod* scriptMethods; }; template std::string Lunar::className = ""; template const ScriptMethod* Lunar::scriptMethods = NULL; #endif /* _LUNAR_H */