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
00026
00027 lua_pushvalue(L, methods);
00028 set(L, LUA_GLOBALSINDEX, ("Lunar_" + std::string(T::className)).c_str());
00029
00030
00031 lua_pushvalue(L, methods);
00032 set(L, metatable, "__metatable");
00033
00034
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);
00062 lua_pushcfunction(L, new_T);
00063 lua_pushvalue(L, -1);
00064 set(L, methods, "new");
00065 set(L, -3, "__call");
00066 lua_setmetatable(L, methods);
00067
00068
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);
00077 }
00078
00079
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;
00084 if (!luaL_checkudata(L, base, ("Lunar_" + std::string(T::className)).c_str())) {
00085 lua_settop(L, base-1);
00086 lua_pushfstring(L, "not a valid %s userdata", T::className);
00087 return -1;
00088 }
00089
00090 lua_pushstring(L, method);
00091 lua_gettable(L, base);
00092 if (lua_isnil(L, -1)) {
00093 lua_settop(L, base-1);
00094 lua_pushfstring(L, "%s missing method '%s'", T::className, method);
00095 return -1;
00096 }
00097 lua_insert(L, base);
00098
00099 int status = lua_pcall(L, 1+nargs, nresults, errfunc);
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);
00106 return -1;
00107 }
00108 return lua_gettop(L) - base + 1;
00109 }
00110
00111
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());
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;
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;
00136 }
00137
00138
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;
00144 }
00145
00146 static T *wide_check(lua_State *L, int narg) {
00147 void *p = lua_touserdata(L, narg);
00148 if (p != NULL) {
00149 if (lua_getmetatable(L, narg)) {
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());
00156 if (lua_rawequal(L, -1, -2)) {
00157 lua_pop(L, 2);
00158 return static_cast<userdataType*>(p)->pT;
00159 } else {
00160 lua_pop(L, 1);
00161 }
00162 ++i;
00163 className = T::classList[i];
00164 }
00165 } else {
00166 lua_getfield(L, LUA_REGISTRYINDEX, ("Lunar_" + std::string(T::className)).c_str());
00167 if (lua_rawequal(L, -1, -2)) {
00168 lua_pop(L, 2);
00169 return static_cast<userdataType*>(p)->pT;
00170 } else {
00171 lua_pop(L, 1);
00172 }
00173 }
00174 lua_pop(L, 1);
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();
00189
00190
00191 static int thunk(lua_State *L) {
00192
00193 T *obj = check(L, 1);
00194 lua_remove(L, 1);
00195
00196 RegType *l = static_cast<RegType*>(lua_touserdata(L, lua_upvalueindex(1)));
00197 return (obj->*(l->mfunc))(L);
00198 }
00199
00200
00201
00202 static int new_T(lua_State *L) {
00203 lua_remove(L, 1);
00204 T *obj = new T(L);
00205 push(L, obj, true);
00206 return 1;
00207 }
00208
00209
00210 static int gc_T(lua_State *L) {
00211 if (luaL_getmetafield(L, 1, "do not trash")) {
00212 lua_pushvalue(L, 1);
00213 lua_gettable(L, -2);
00214 if (!lua_isnil(L, -1)) return 0;
00215 }
00216 userdataType *ud = static_cast<userdataType*>(lua_touserdata(L, 1));
00217 T *obj = ud->pT;
00218 if (obj) delete obj;
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);
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);
00240 lua_setmetatable(L, -2);
00241 lua_pushliteral(L, "__mode");
00242 lua_pushstring(L, mode);
00243 lua_settable(L, -3);
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);
00263 if (lua_isnil(L, -1)) {
00264 lua_pop(L, 1);
00265 lua_checkstack(L, 3);
00266 ud = lua_newuserdata(L, sz);
00267 lua_pushlightuserdata(L, key);
00268 lua_pushvalue(L, -2);
00269 lua_settable(L, -4);
00270 }
00271 return ud;
00272 }
00273 };
00274
00275 # endif