Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 8972 was 8408, checked in by bensch, 18 years ago

trunk: merged the script_engine branche back here
merged with command
svn merge https://svn.orxonox.net/orxonox/branches/script_engine . -r8284:HEAD
no conflicts

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* 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    Executor *l = static_cast<Executor*>(lua_touserdata(L, lua_upvalueindex(1)));
187    int value;
188    (*l)(obj, value, L);  // call member function
189    return value;
190  }
191
192  // create a new T object and
193  // push onto the Lua stack a userdata containing a pointer to T object
194  static int new_T(lua_State *L)
195  {
196    lua_remove(L, 1);   // use classname:new(), instead of classname.new()
197    T *obj = new T();  // call constructor for T objects
198    push(L, obj, true); // gc_T will delete this object
199    return 1;           // userdata containing pointer to T object
200  }
201
202  // garbage collection metamethod
203  static int gc_T(lua_State *L)
204  {
205    if (luaL_getmetafield(L, 1, "do not trash"))
206    {
207      lua_pushvalue(L, 1);  // dup userdata
208      lua_gettable(L, -2);
209      if (!lua_isnil(L, -1)) return 0;  // do not delete object
210    }
211    userdataType *ud = static_cast<userdataType*>(lua_touserdata(L, 1));
212    T *obj = ud->pT;
213    if (obj) delete obj;  // call destructor for T objects
214    return 0;
215  }
216
217  static int tostring_T (lua_State *L)
218  {
219    char buff[32];
220    userdataType *ud = static_cast<userdataType*>(lua_touserdata(L, 1));
221    T *obj = ud->pT;
222    sprintf(buff, "%p", obj);
223    lua_pushfstring(L, "%s (%s)", Lunar<T>::className.c_str(), buff);
224    return 1;
225  }
226
227  static void set(lua_State *L, int table_index, const char *key)
228  {
229    lua_pushstring(L, key);
230    lua_insert(L, -2);  // swap value and key
231    lua_settable(L, table_index);
232  }
233
234  static void weaktable(lua_State *L, const char *mode)
235  {
236    lua_newtable(L);
237    lua_pushvalue(L, -1);  // table is its own metatable
238    lua_setmetatable(L, -2);
239    lua_pushliteral(L, "__mode");
240    lua_pushstring(L, mode);
241    lua_settable(L, -3);   // metatable.__mode = mode
242  }
243
244  static void subtable(lua_State *L, int tindex, const char *name, const char *mode)
245  {
246    lua_pushstring(L, name);
247    lua_gettable(L, tindex);
248    if (lua_isnil(L, -1))
249    {
250      lua_pop(L, 1);
251      lua_checkstack(L, 3);
252      weaktable(L, mode);
253      lua_pushstring(L, name);
254      lua_pushvalue(L, -2);
255      lua_settable(L, tindex);
256    }
257  }
258
259  static void *pushuserdata(lua_State *L, void *key, size_t sz)
260  {
261    void *ud = 0;
262    lua_pushlightuserdata(L, key);
263    lua_gettable(L, -2);     // lookup[key]
264    if (lua_isnil(L, -1))
265    {
266      lua_pop(L, 1);         // drop nil
267      lua_checkstack(L, 3);
268      ud = lua_newuserdata(L, sz);  // create new userdata
269      lua_pushlightuserdata(L, key);
270      lua_pushvalue(L, -2);  // dup userdata
271      lua_settable(L, -4);   // lookup[key] = userdata
272    }
273    return ud;
274  }
275
276  private:
277    static std::string className;
278    static const ScriptMethod* scriptMethods;
279};
280
281template <typename T>
282    std::string Lunar<T>::className = "";
283template <typename T>
284    const ScriptMethod* Lunar<T>::scriptMethods = NULL;
285
286#endif /* _LUNAR_H */
287
Note: See TracBrowser for help on using the repository browser.