Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/branches/script_engine/src/lib/script_engine/lunar.h @ 7821

Last change on this file since 7821 was 7821, checked in by snellen, 18 years ago

should compile now

File size: 7.5 KB
Line 
1    #include "luaincl.h"
2
3    template <typename T> class Lunar {
4      typedef struct { T *pT; } userdataType;
5    public:
6      typedef int (T::*mfp)(lua_State *L);
7      typedef struct { const char *name; mfp mfunc; } RegType;
8
9      static void Register(lua_State *L) {
10        lua_newtable(L);
11        int methods = lua_gettop(L);
12
13        luaL_newmetatable(L, T::className);
14        int metatable = lua_gettop(L);
15
16        // store method table in globals so that
17        // scripts can add functions written in Lua.
18        lua_pushvalue(L, methods);
19        set(L, LUA_GLOBALSINDEX, T::className);
20
21        // hide metatable from Lua getmetatable()
22        lua_pushvalue(L, methods);
23        set(L, metatable, "__metatable");
24
25        lua_pushvalue(L, methods);
26        set(L, metatable, "__index");
27
28        lua_pushcfunction(L, tostring_T);
29        set(L, metatable, "__tostring");
30
31        lua_pushcfunction(L, gc_T);
32        set(L, metatable, "__gc");
33
34        lua_newtable(L);                // mt for method table
35        lua_pushcfunction(L, new_T);
36        lua_pushvalue(L, -1);           // dup new_T function
37        set(L, methods, "new");         // add new_T to method table
38        set(L, -3, "__call");           // mt.__call = new_T
39        lua_setmetatable(L, methods);
40
41        // fill method table with methods from class T
42        for (RegType *l = T::methods; l->name; l++) {
43          lua_pushstring(L, l->name);
44          lua_pushlightuserdata(L, (void*)l);
45          lua_pushcclosure(L, thunk, 1);
46          lua_settable(L, methods);
47        }
48
49        lua_pop(L, 2);  // drop metatable and method table
50      }
51
52      // call named lua method from userdata method table
53      static int call(lua_State *L, const char *method,
54                      int nargs=0, int nresults=LUA_MULTRET, int errfunc=0)
55      {
56        int base = lua_gettop(L) - nargs;  // userdata index
57        if (!luaL_checkudata(L, base, T::className)) {
58          lua_settop(L, base-1);           // drop userdata and args
59          lua_pushfstring(L, "not a valid %s userdata", T::className);
60          return -1;
61        }
62
63        lua_pushstring(L, method);         // method name
64        lua_gettable(L, base);             // get method from userdata
65        if (lua_isnil(L, -1)) {            // no method?
66          lua_settop(L, base-1);           // drop userdata and args
67          lua_pushfstring(L, "%s missing method '%s'", T::className, method);
68          return -1;
69        }
70        lua_insert(L, base);               // put method under userdata, args
71
72        int status = lua_pcall(L, 1+nargs, nresults, errfunc);  // call method
73        if (status) {
74          const char *msg = lua_tostring(L, -1);
75          if (msg == NULL) msg = "(error with no message)";
76          lua_pushfstring(L, "%s:%s status = %d\n%s",
77                          T::className, method, status, msg);
78          lua_remove(L, base);             // remove old message
79          return -1;
80        }
81        return lua_gettop(L) - base + 1;   // number of results
82      }
83
84      // push onto the Lua stack a userdata containing a pointer to T object
85      static int push(lua_State *L, T *obj, bool gc=false) {
86        if (!obj) { lua_pushnil(L); return 0; }
87        luaL_getmetatable(L, T::className);  // lookup metatable in Lua registry
88        if (lua_isnil(L, -1)) luaL_error(L, "%s missing metatable", T::className);
89        int mt = lua_gettop(L);
90        subtable(L, mt, "userdata", "v");
91        userdataType *ud =
92          static_cast<userdataType*>(pushuserdata(L, obj, sizeof(userdataType)));
93        if (ud) {
94          ud->pT = obj;  // store pointer to object in userdata
95          lua_pushvalue(L, mt);
96          lua_setmetatable(L, -2);
97          if (gc == false) {
98            lua_checkstack(L, 3);
99            subtable(L, mt, "do not trash", "k");
100            lua_pushvalue(L, -2);
101            lua_pushboolean(L, 1);
102            lua_settable(L, -3);
103            lua_pop(L, 1);
104          }
105        }
106        lua_replace(L, mt);
107        lua_settop(L, mt);
108        return mt;  // index of userdata containing pointer to T object
109      }
110
111      // get userdata from Lua stack and return pointer to T object
112      static T *check(lua_State *L, int narg) {
113        userdataType *ud =
114          static_cast<userdataType*>(luaL_checkudata(L, narg, T::className));
115        if(!ud) luaL_typerror(L, narg, T::className);
116        return ud->pT;  // pointer to T object
117      }
118
119    private:
120      Lunar();  // hide default constructor
121
122      // member function dispatcher
123      static int thunk(lua_State *L) {
124        // stack has userdata, followed by method args
125        T *obj = check(L, 1);  // get 'self', or if you prefer, 'this'
126        lua_remove(L, 1);  // remove self so member function args start at index 1
127        // get member function from upvalue
128        RegType *l = static_cast<RegType*>(lua_touserdata(L, lua_upvalueindex(1)));
129        return (obj->*(l->mfunc))(L);  // call member function
130      }
131
132      // create a new T object and
133      // push onto the Lua stack a userdata containing a pointer to T object
134      static int new_T(lua_State *L) {
135        lua_remove(L, 1);   // use classname:new(), instead of classname.new()
136        T *obj = new T(L);  // call constructor for T objects
137        push(L, obj, true); // gc_T will delete this object
138        return 1;           // userdata containing pointer to T object
139      }
140
141      // garbage collection metamethod
142      static int gc_T(lua_State *L) {
143        if (luaL_getmetafield(L, 1, "do not trash")) {
144          lua_pushvalue(L, 1);  // dup userdata
145          lua_gettable(L, -2);
146          if (!lua_isnil(L, -1)) return 0;  // do not delete object
147        }
148        userdataType *ud = static_cast<userdataType*>(lua_touserdata(L, 1));
149        T *obj = ud->pT;
150        if (obj) delete obj;  // call destructor for T objects
151        return 0;
152      }
153
154      static int tostring_T (lua_State *L) {
155        char buff[32];
156        userdataType *ud = static_cast<userdataType*>(lua_touserdata(L, 1));
157        T *obj = ud->pT;
158        sprintf(buff, "%p", obj);
159        lua_pushfstring(L, "%s (%s)", T::className, buff);
160        return 1;
161      }
162
163      static void set(lua_State *L, int table_index, const char *key) {
164        lua_pushstring(L, key);
165        lua_insert(L, -2);  // swap value and key
166        lua_settable(L, table_index);
167      }
168
169      static void weaktable(lua_State *L, const char *mode) {
170        lua_newtable(L);
171        lua_pushvalue(L, -1);  // table is its own metatable
172        lua_setmetatable(L, -2);
173        lua_pushliteral(L, "__mode");
174        lua_pushstring(L, mode);
175        lua_settable(L, -3);   // metatable.__mode = mode
176      }
177
178      static void subtable(lua_State *L, int tindex, const char *name, const char *mode) {
179        lua_pushstring(L, name);
180        lua_gettable(L, tindex);
181        if (lua_isnil(L, -1)) {
182          lua_pop(L, 1);
183          lua_checkstack(L, 3);
184          weaktable(L, mode);
185          lua_pushstring(L, name);
186          lua_pushvalue(L, -2);
187          lua_settable(L, tindex);
188        }
189      }
190
191      static void *pushuserdata(lua_State *L, void *key, size_t sz) {
192        void *ud = 0;
193        lua_pushlightuserdata(L, key);
194        lua_gettable(L, -2);     // lookup[key]
195        if (lua_isnil(L, -1)) {
196          lua_pop(L, 1);         // drop nil
197          lua_checkstack(L, 3);
198          ud = lua_newuserdata(L, sz);  // create new userdata
199          lua_pushlightuserdata(L, key);
200          lua_pushvalue(L, -2);  // dup userdata
201          lua_settable(L, -4);   // lookup[key] = userdata
202        }
203        return ud;
204      }
205    };
206
207
Note: See TracBrowser for help on using the repository browser.