lua++
|
00001 #ifndef lunar_h 00002 #define lunar_h 00003 00004 extern "C" { 00005 #include <lua.h> 00006 #include <lauxlib.h> 00007 #include <lualib.h> 00008 } 00009 00010 #include <string> 00011 00012 template <typename T> class Lunar { 00013 typedef struct { T *pT; } userdataType; 00014 public: 00015 typedef int (T::*mfp)(lua_State *L); 00016 typedef struct { const char *name; mfp mfunc; } RegType; 00017 00018 static void reg(lua_State *L) { 00019 lua_newtable(L); 00020 int methods = lua_gettop(L); 00021 00022 luaL_newmetatable(L, ("Lunar_" + std::string(T::className)).c_str()); 00023 int metatable = lua_gettop(L); 00024 00025 // store method table in globals so that 00026 // scripts can add functions written in Lua. 00027 lua_pushvalue(L, methods); 00028 set(L, LUA_GLOBALSINDEX, ("Lunar_" + std::string(T::className)).c_str()); 00029 00030 // hide metatable from Lua getmetatable() 00031 lua_pushvalue(L, methods); 00032 set(L, metatable, "__metatable"); 00033 00034 // Creating _index and _newindex functions 00035 std::string class_funcs = ""; 00036 class_funcs += "function Lunar_" + std::string(T::className) + "_index(t, k)\n"; 00037 class_funcs += " local r = rawget(Lunar_" + std::string(T::className) + ", k)\n"; 00038 class_funcs += " if r ~= nil then return r end\n"; 00039 class_funcs += " local dt = t:dt()\n"; 00040 class_funcs += " return rawget(dt, k)\n"; 00041 class_funcs += "end\n"; 00042 class_funcs += "function Lunar_" + std::string(T::className) + "_newindex(t, k, v)\n"; 00043 class_funcs += " local dt = t:dt()\n"; 00044 class_funcs += " dt[k] = v\n"; 00045 class_funcs += "end\n"; 00046 00047 luaL_dostring(L, class_funcs.c_str()); 00048 00049 lua_getglobal(L, ("Lunar_" + std::string(T::className) + "_index").c_str()); 00050 set(L, metatable, "__index"); 00051 00052 lua_getglobal(L, ("Lunar_" + std::string(T::className) + "_newindex").c_str()); 00053 set(L, metatable, "__newindex"); 00054 00055 lua_pushcfunction(L, tostring_T); 00056 set(L, metatable, "__tostring"); 00057 00058 lua_pushcfunction(L, gc_T); 00059 set(L, metatable, "__gc"); 00060 00061 lua_newtable(L); // mt for method table 00062 lua_pushcfunction(L, new_T); 00063 lua_pushvalue(L, -1); // dup new_T function 00064 set(L, methods, "new"); // add new_T to method table 00065 set(L, -3, "__call"); // mt.__call = new_T 00066 lua_setmetatable(L, methods); 00067 00068 // fill method table with methods from class T 00069 for (RegType *l = T::methods; l->name; l++) { 00070 lua_pushstring(L, l->name); 00071 lua_pushlightuserdata(L, (void*)l); 00072 lua_pushcclosure(L, thunk, 1); 00073 lua_settable(L, methods); 00074 } 00075 00076 lua_pop(L, 2); // drop metatable and method table 00077 } 00078 00079 // call named lua method from userdata method table 00080 static int call(lua_State *L, const char *method, 00081 int nargs=0, int nresults=LUA_MULTRET, int errfunc=0) 00082 { 00083 int base = lua_gettop(L) - nargs; // userdata index 00084 if (!luaL_checkudata(L, base, ("Lunar_" + std::string(T::className)).c_str())) { 00085 lua_settop(L, base-1); // drop userdata and args 00086 lua_pushfstring(L, "not a valid %s userdata", T::className); 00087 return -1; 00088 } 00089 00090 lua_pushstring(L, method); // method name 00091 lua_gettable(L, base); // get method from userdata 00092 if (lua_isnil(L, -1)) { // no method? 00093 lua_settop(L, base-1); // drop userdata and args 00094 lua_pushfstring(L, "%s missing method '%s'", T::className, method); 00095 return -1; 00096 } 00097 lua_insert(L, base); // put method under userdata, args 00098 00099 int status = lua_pcall(L, 1+nargs, nresults, errfunc); // call method 00100 if (status) { 00101 const char *msg = lua_tostring(L, -1); 00102 if (msg == NULL) msg = "(error with no message)"; 00103 lua_pushfstring(L, "%s:%s status = %d\n%s", 00104 T::className, method, status, msg); 00105 lua_remove(L, base); // remove old message 00106 return -1; 00107 } 00108 return lua_gettop(L) - base + 1; // number of results 00109 } 00110 00111 // push onto the Lua stack a userdata containing a pointer to T object 00112 static int push(lua_State *L, T *obj, bool gc=false) { 00113 if (!obj) { lua_pushnil(L); return 0; } 00114 luaL_getmetatable(L, ("Lunar_" + std::string(T::className)).c_str()); // lookup metatable in Lua registry 00115 if (lua_isnil(L, -1)) luaL_error(L, "%s missing metatable", T::className); 00116 int mt = lua_gettop(L); 00117 subtable(L, mt, "userdata", "v"); 00118 userdataType *ud = 00119 static_cast<userdataType*>(pushuserdata(L, obj, sizeof(userdataType))); 00120 if (ud) { 00121 ud->pT = obj; // store pointer to object in userdata 00122 lua_pushvalue(L, mt); 00123 lua_setmetatable(L, -2); 00124 if (gc == false) { 00125 lua_checkstack(L, 3); 00126 subtable(L, mt, "do not trash", "k"); 00127 lua_pushvalue(L, -2); 00128 lua_pushboolean(L, 1); 00129 lua_settable(L, -3); 00130 lua_pop(L, 1); 00131 } 00132 } 00133 lua_replace(L, mt); 00134 lua_settop(L, mt); 00135 return mt; // index of userdata containing pointer to T object 00136 } 00137 00138 // get userdata from Lua stack and return pointer to T object 00139 static T *check(lua_State *L, int narg) { 00140 userdataType *ud = 00141 static_cast<userdataType*>(luaL_checkudata(L, narg, ("Lunar_" + std::string(T::className)).c_str())); 00142 if(!ud) luaL_typerror(L, narg, ("Lunar_" + std::string(T::className)).c_str()); 00143 return ud->pT; // pointer to T object 00144 } 00145 00146 static T *wide_check(lua_State *L, int narg) { 00147 void *p = lua_touserdata(L, narg); 00148 if (p != NULL) { /* value is a userdata? */ 00149 if (lua_getmetatable(L, narg)) { /* does it have a metatable? */ 00150 if (T::classList[0]) { 00151 int i = 0; 00152 const char* className = T::classList[0]; 00153 while (className) 00154 { 00155 lua_getfield(L, LUA_REGISTRYINDEX, ("Lunar_" + std::string(className)).c_str()); /* get possible metatable */ 00156 if (lua_rawequal(L, -1, -2)) { /* is it the same as the current one? */ 00157 lua_pop(L, 2); /* remove both metatables */ 00158 return static_cast<userdataType*>(p)->pT; 00159 } else { 00160 lua_pop(L, 1); /* remove the temp metatable */ 00161 } 00162 ++i; 00163 className = T::classList[i]; 00164 } 00165 } else { 00166 lua_getfield(L, LUA_REGISTRYINDEX, ("Lunar_" + std::string(T::className)).c_str()); /* get correct metatable */ 00167 if (lua_rawequal(L, -1, -2)) { /* does it have the correct mt? */ 00168 lua_pop(L, 2); /* remove both metatables */ 00169 return static_cast<userdataType*>(p)->pT; 00170 } else { 00171 lua_pop(L, 1); /* remove the temp metatable */ 00172 } 00173 } 00174 lua_pop(L, 1); /* remove the metatable */ 00175 } 00176 } 00177 return NULL; 00178 } 00179 00180 static T *push_new(lua_State *L) 00181 { 00182 T *obj = new T(L); 00183 push(L, obj, true); 00184 return obj; 00185 } 00186 00187 private: 00188 Lunar(); // hide default constructor 00189 00190 // member function dispatcher 00191 static int thunk(lua_State *L) { 00192 // stack has userdata, followed by method args 00193 T *obj = check(L, 1); // get 'self', or if you prefer, 'this' 00194 lua_remove(L, 1); // remove self so member function args start at index 1 00195 // get member function from upvalue 00196 RegType *l = static_cast<RegType*>(lua_touserdata(L, lua_upvalueindex(1))); 00197 return (obj->*(l->mfunc))(L); // call member function 00198 } 00199 00200 // create a new T object and 00201 // push onto the Lua stack a userdata containing a pointer to T object 00202 static int new_T(lua_State *L) { 00203 lua_remove(L, 1); // use classname:new(), instead of classname.new() 00204 T *obj = new T(L); // call constructor for T objects 00205 push(L, obj, true); // gc_T will delete this object 00206 return 1; // userdata containing pointer to T object 00207 } 00208 00209 // garbage collection metamethod 00210 static int gc_T(lua_State *L) { 00211 if (luaL_getmetafield(L, 1, "do not trash")) { 00212 lua_pushvalue(L, 1); // dup userdata 00213 lua_gettable(L, -2); 00214 if (!lua_isnil(L, -1)) return 0; // do not delete object 00215 } 00216 userdataType *ud = static_cast<userdataType*>(lua_touserdata(L, 1)); 00217 T *obj = ud->pT; 00218 if (obj) delete obj; // call destructor for T objects 00219 return 0; 00220 } 00221 00222 static int tostring_T (lua_State *L) { 00223 char buff[32]; 00224 userdataType *ud = static_cast<userdataType*>(lua_touserdata(L, 1)); 00225 T *obj = ud->pT; 00226 sprintf(buff, "%p", obj); 00227 lua_pushfstring(L, "%s (%s)", T::className, buff); 00228 return 1; 00229 } 00230 00231 static void set(lua_State *L, int table_index, const char *key) { 00232 lua_pushstring(L, key); 00233 lua_insert(L, -2); // swap value and key 00234 lua_settable(L, table_index); 00235 } 00236 00237 static void weaktable(lua_State *L, const char *mode) { 00238 lua_newtable(L); 00239 lua_pushvalue(L, -1); // table is its own metatable 00240 lua_setmetatable(L, -2); 00241 lua_pushliteral(L, "__mode"); 00242 lua_pushstring(L, mode); 00243 lua_settable(L, -3); // metatable.__mode = mode 00244 } 00245 00246 static void subtable(lua_State *L, int tindex, const char *name, const char *mode) { 00247 lua_pushstring(L, name); 00248 lua_gettable(L, tindex); 00249 if (lua_isnil(L, -1)) { 00250 lua_pop(L, 1); 00251 lua_checkstack(L, 3); 00252 weaktable(L, mode); 00253 lua_pushstring(L, name); 00254 lua_pushvalue(L, -2); 00255 lua_settable(L, tindex); 00256 } 00257 } 00258 00259 static void *pushuserdata(lua_State *L, void *key, size_t sz) { 00260 void *ud = 0; 00261 lua_pushlightuserdata(L, key); 00262 lua_gettable(L, -2); // lookup[key] 00263 if (lua_isnil(L, -1)) { 00264 lua_pop(L, 1); // drop nil 00265 lua_checkstack(L, 3); 00266 ud = lua_newuserdata(L, sz); // create new userdata 00267 lua_pushlightuserdata(L, key); 00268 lua_pushvalue(L, -2); // dup userdata 00269 lua_settable(L, -4); // lookup[key] = userdata 00270 } 00271 return ud; 00272 } 00273 }; 00274 00275 # endif