Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

Works… well almost

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