Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

finished Script.cc

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