Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

adapted filename

File size: 8.2 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
139      static int insertObject(Script* script, T* obj, const std::string& name, bool gc=false)
140      {
141        if(script != NULL)
142          return insertObject(script->getLuaState(), obj, name, gc);
143
144
145      }
146
147      // get userdata from Lua stack and return pointer to T object
148      static T *check(lua_State *L, int narg) {
149        userdataType *ud =
150          static_cast<userdataType*>(luaL_checkudata(L, narg, T::className));
151        if(!ud) luaL_typerror(L, narg, T::className);
152        return ud->pT;  // pointer to T object
153      }
154
155    private:
156      Lunar();  // hide default constructor
157
158      // member function dispatcher
159      static int thunk(lua_State *L) {
160        // stack has userdata, followed by method args
161        T *obj = check(L, 1);  // get 'self', or if you prefer, 'this'
162        lua_remove(L, 1);  // remove self so member function args start at index 1
163        // get member function from upvalue
164        RegType *l = static_cast<RegType*>(lua_touserdata(L, lua_upvalueindex(1)));
165        return (obj->*(l->mfunc))(L);  // call member function
166      }
167
168      // create a new T object and
169      // push onto the Lua stack a userdata containing a pointer to T object
170      static int new_T(lua_State *L) {
171        lua_remove(L, 1);   // use classname:new(), instead of classname.new()
172        T *obj = new T(L);  // call constructor for T objects
173        push(L, obj, true); // gc_T will delete this object
174        return 1;           // userdata containing pointer to T object
175      }
176
177      // garbage collection metamethod
178      static int gc_T(lua_State *L) {
179        if (luaL_getmetafield(L, 1, "do not trash")) {
180          lua_pushvalue(L, 1);  // dup userdata
181          lua_gettable(L, -2);
182          if (!lua_isnil(L, -1)) return 0;  // do not delete object
183        }
184        userdataType *ud = static_cast<userdataType*>(lua_touserdata(L, 1));
185        T *obj = ud->pT;
186        if (obj) delete obj;  // call destructor for T objects
187        return 0;
188      }
189
190      static int tostring_T (lua_State *L) {
191        char buff[32];
192        userdataType *ud = static_cast<userdataType*>(lua_touserdata(L, 1));
193        T *obj = ud->pT;
194        sprintf(buff, "%p", obj);
195        lua_pushfstring(L, "%s (%s)", T::className, buff);
196        return 1;
197      }
198
199      static void set(lua_State *L, int table_index, const char *key) {
200        lua_pushstring(L, key);
201        lua_insert(L, -2);  // swap value and key
202        lua_settable(L, table_index);
203      }
204
205      static void weaktable(lua_State *L, const char *mode) {
206        lua_newtable(L);
207        lua_pushvalue(L, -1);  // table is its own metatable
208        lua_setmetatable(L, -2);
209        lua_pushliteral(L, "__mode");
210        lua_pushstring(L, mode);
211        lua_settable(L, -3);   // metatable.__mode = mode
212      }
213
214      static void subtable(lua_State *L, int tindex, const char *name, const char *mode) {
215        lua_pushstring(L, name);
216        lua_gettable(L, tindex);
217        if (lua_isnil(L, -1)) {
218          lua_pop(L, 1);
219          lua_checkstack(L, 3);
220          weaktable(L, mode);
221          lua_pushstring(L, name);
222          lua_pushvalue(L, -2);
223          lua_settable(L, tindex);
224        }
225      }
226
227      static void *pushuserdata(lua_State *L, void *key, size_t sz) {
228        void *ud = 0;
229        lua_pushlightuserdata(L, key);
230        lua_gettable(L, -2);     // lookup[key]
231        if (lua_isnil(L, -1)) {
232          lua_pop(L, 1);         // drop nil
233          lua_checkstack(L, 3);
234          ud = lua_newuserdata(L, sz);  // create new userdata
235          lua_pushlightuserdata(L, key);
236          lua_pushvalue(L, -2);  // dup userdata
237          lua_settable(L, -4);   // lookup[key] = userdata
238        }
239        return ud;
240      }
241    };
242
243#endif /* _LUNAR_H */
244
Note: See TracBrowser for help on using the repository browser.