Luna.hh

00001 /*
00002 ** TowBowlTactics, a turn-based strategy football game.
00003 **
00004 ** Copyright (C) 2009-2010 The TBT Team.
00005 **
00006 ** This program is free software; you can redistribute it and/or
00007 ** modify it under the terms of the GNU General Public License
00008 ** as published by the Free Software Foundation; either version 2
00009 ** of the License, or (at your option) any later version.
00010 **
00011 ** The complete GNU General Public Licence Notice can be found as the
00012 ** `NOTICE' file in the root directory.
00013 **
00014 ** The TBT Team consists of people listed in the `AUTHORS' file.
00015 */
00016 
00017 #ifndef LUNA_HH_
00018 # define LUNA_HH_
00019 
00020 # include "Lua.hh"
00021 
00022 # include <cstring>
00023 # include <string>
00024 
00032 template <typename T>
00033 class Luna
00034 {
00035   typedef struct { T *pT; } userdataType;
00036 
00037 public:
00038   typedef int (T::*mfp)(lua_State *L);
00039 
00040   struct RegType {
00041     const char *name;
00042     mfp mfunc;
00043   };
00044 
00046   static void registerMethods(lua_State* l);
00047   
00049   static T* check(lua_State* l, int narg);
00050 
00052   static T* create(lua_State* l);
00053 
00057   static int getMethod(lua_State *l, const std::string& funcname);
00058 
00059 
00060 private:
00061   Luna();  // hide default constructor
00062 
00063   // member function dispatcher
00064   static int thunk(lua_State *L);
00065 
00066   // create a new T object and
00067   // push onto the Lua stack a userdata containing a pointer to T object
00068   static int new_T(lua_State *L);
00069 
00070   // garbage collection metamethod
00071   static int gc_T(lua_State *L);
00072 
00073   static int tostring_T(lua_State *L);
00074 
00075   // add lua function to this userdata
00076   static int newindex(lua_State* l);
00077 };
00078 
00079 # define luna_method(class, name) { #name, &class::name }
00080 
00081 
00082 
00083 
00084 //
00085 // implementation of the Luna class
00086 //
00087 
00088 
00089 template <typename T>
00090 inline void Luna<T>::registerMethods(lua_State *l)
00091 {
00092   std::string name(T::className);
00093   size_t idx = name.find_last_of(".");
00094   std::string superclass(name.substr(0, idx));
00095 
00096   lua_newtable(l);
00097   int methods = lua_gettop(l);
00098 
00099   luaL_newmetatable(l, T::className);
00100   int metatable = lua_gettop(l);
00101 
00102   // store method table in globals so that
00103   // scripts can add functions written in Lua.
00104   if (idx != std::string::npos)
00105     lua_pushstring(l, name.substr(idx + 1).c_str());
00106   else
00107     lua_pushstring(l, T::className);
00108   lua_pushvalue(l, methods);
00109   lua_settable(l, LUA_GLOBALSINDEX);
00110 
00111   lua_pushliteral(l, "__metatable");
00112   lua_pushvalue(l, methods);
00113   lua_settable(l, metatable);  // hide metatable from Lua getmetatable()
00114 
00115   lua_pushcclosure(l, newindex, 0);
00116   lua_setfield(l, metatable, "__newindex");
00117 
00118   lua_pushvalue(l, methods);
00119   lua_setfield(l, metatable, "__index");
00120 
00121   lua_pushliteral(l, "__tostring");
00122   lua_pushcfunction(l, tostring_T);
00123   lua_settable(l, metatable);
00124 
00125   lua_pushliteral(l, "__gc");
00126   lua_pushcfunction(l, gc_T);
00127   lua_settable(l, metatable);
00128 
00129   lua_newtable(l);                // mt for method table
00130   int mt = lua_gettop(l);
00131   lua_pushliteral(l, "__call");
00132   lua_pushcfunction(l, new_T);
00133   lua_pushliteral(l, "new");
00134   lua_pushvalue(l, -2);           // dup new_T function
00135   lua_settable(l, methods);       // add new_T to method table
00136   lua_settable(l, mt);            // mt.__call = new_T
00137   lua_setmetatable(l, methods);
00138 
00139   // fill method table with methods from class T
00140   for (RegType *ll = T::methods; ll->name; ll++)
00141     {
00142       lua_pushstring(l, ll->name);
00143       lua_pushlightuserdata(l, (void*)ll);
00144       lua_pushcclosure(l, thunk, 1);
00145       lua_settable(l, methods);
00146     }
00147 
00148   // add methods from base class
00149   if (idx != std::string::npos)
00150     {
00151       luaL_getmetatable(l, superclass.c_str());
00152       if (!lua_istable(l, -1))
00153         luaL_error(l, "superclass '%s' not found", superclass.c_str());
00154       lua_getfield(l, -1, "__index");
00155 
00156       lua_pushnil(l);
00157       while (lua_next(l, -2))
00158         {
00159           if (strcmp(lua_tostring(l, -2), "new"))
00160             {
00161               lua_pushvalue(l, -2);
00162               lua_pushvalue(l, -2);
00163               lua_settable(l, methods);
00164             }
00165           lua_pop(l, 1);
00166         }
00167       lua_pop(l, 2);
00168     }
00169 
00170   lua_pop(l, 2);  // drop metatable and method table
00171 }
00172 
00173 template <typename T>
00174 inline T* Luna<T>::check(lua_State *L, int narg)
00175 {
00176   userdataType *ud;
00177 
00178   ud = static_cast<userdataType*>(luaL_checkudata(L, narg, T::className));
00179   if (!ud)
00180     luaL_typerror(L, narg, T::className);
00181   return ud->pT;  // pointer to T object
00182 }
00183 
00184 template <typename T>
00185 inline T* Luna<T>::create(lua_State *l)
00186 {
00187   lua_pushnil(l);
00188   lua_insert(l, 1);
00189   new_T(l);
00190   return check(l, -1);
00191 }
00192 
00193 template <typename T>
00194 inline int Luna<T>::getMethod(lua_State *l, const std::string& funcname)
00195 {
00196   luaL_getmetatable(l, T::className);
00197   lua_getfield(l, -1, "__index");
00198   lua_remove(l, -2);
00199   lua_getfield(l, -1, funcname.c_str());
00200   lua_remove(l, -2);
00201 
00202   if (!lua_isfunction(l, -1))
00203     {
00204       // not a function, pop it
00205       lua_pop(l, 1);
00206       return luaL_error(l, "method not found in %s: %s",
00207                         T::className, funcname.c_str());
00208     }
00209 
00210   // ok, push function
00211   return 1;
00212 }
00213 
00214 
00215 
00216 template <typename T>
00217 inline int Luna<T>::thunk(lua_State *L)
00218 {
00219   // stack has userdata, followed by method args
00220   T *obj = check(L, 1);  // get 'self', or if you prefer, 'this'
00221   lua_remove(L, 1);  // remove self so member function args start at index 1
00222   // get member function from upvalue
00223   RegType *l = static_cast<RegType*>(lua_touserdata(L, lua_upvalueindex(1)));
00224   return (obj->*(l->mfunc))(L);  // call member function
00225 }
00226 
00227 template <typename T>
00228 inline int Luna<T>::new_T(lua_State *L)
00229 {
00230   lua_remove(L, 1);  // use classname:new(), instead of classname.new()
00231   T *obj = new T(L);  // call constructor for T objects
00232   userdataType *ud =
00233     static_cast<userdataType*>(lua_newuserdata(L, sizeof(userdataType)));
00234   ud->pT = obj;  // store pointer to object in userdata
00235   luaL_getmetatable(L, T::className);  // lookup metatable in Lua registry
00236   lua_setmetatable(L, -2);
00237   return 1;  // userdata containing pointer to T object
00238 }
00239 
00240 template <typename T>
00241 inline int Luna<T>::gc_T(lua_State *L)
00242 {
00243   userdataType *ud = static_cast<userdataType*>(lua_touserdata(L, 1));
00244   T *obj = ud->pT;
00245   delete obj;  // call destructor for T objects
00246   return 0;
00247 }
00248 
00249 template <typename T>
00250 inline int Luna<T>::tostring_T (lua_State *L)
00251 {
00252   userdataType *ud = static_cast<userdataType*>(lua_touserdata(L, 1));
00253   T *obj = ud->pT;
00254   lua_pushfstring(L, "%s (%p)", T::className, obj);
00255   return 1;
00256 }
00257 
00258 template <typename T>
00259 inline int Luna<T>::newindex(lua_State* l)
00260 {
00261   if (!lua_isfunction(l, 3))
00262     return luaL_error(l, "attempt to set a value to class %s",
00263                       T::className);
00264 
00265   // HACK: add method to the base class
00266   std::string name(T::className);
00267   size_t idx = name.find_last_of(".");
00268   std::string superclass(name.substr(0, idx));
00269 
00270   // retrieve __index in this user metatable
00271   luaL_getmetatable(l, superclass.c_str());
00272   lua_getfield(l, -1, "__index");
00273   if (!lua_istable(l, -1))
00274     return luaL_error(l, "newindex: bug");
00275 
00276   // add the new function to the table
00277   lua_pushvalue(l, 2);
00278   lua_pushvalue(l, 3);
00279   lua_settable(l, -3);
00280   
00281   return 0;
00282 }
00283 
00284 
00285 #endif // LUNA_HH_
Generated on Mon Apr 5 21:17:12 2010 for Stechec/TBT by  doxygen 1.6.3