lua++
C:/Documents and Settings/Kalith/My Documents/Programmation/luapp/include/lunar.hpp
Go to the documentation of this file.
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