Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/trunk/src/lib/script_engine/lunar.h @ 9879

Last change on this file since 9879 was 9869, checked in by bensch, 18 years ago

orxonox/trunk: merged the new_class_id branche back to the trunk.
merged with command:
svn merge https://svn.orxonox.net/orxonox/branches/new_class_id trunk -r9683:HEAD
no conflicts… puh..

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