Cafu Engine
UniScriptState.hpp
1 /*
2 Cafu Engine, http://www.cafu.de/
3 Copyright (c) Carsten Fuchs and other contributors.
4 This project is licensed under the terms of the MIT license.
5 */
6 
7 #ifndef CAFU_UNI_SCRIPT_STATE_HPP_INCLUDED
8 #define CAFU_UNI_SCRIPT_STATE_HPP_INCLUDED
9 
10 #include "Templates/Array.hpp"
11 #include "Templates/Pointer.hpp"
12 #include "TypeSys.hpp"
13 
14 extern "C"
15 {
16  #include <lua.h>
17  #include <lualib.h>
18  #include <lauxlib.h>
19 }
20 
21 #include <cstdarg>
22 #include <string>
23 
24 
25 namespace cf
26 {
27  /// This class checks if the Lua stack has the same size at the start and the end of a function.
29  {
30  public:
31 
32  StackCheckerT(lua_State* LuaState, int Change=0)
33  : m_LuaState(LuaState),
34  m_StartTop(lua_gettop(m_LuaState) + Change)
35  {
36  }
37 
38  ~StackCheckerT()
39  {
40  assert(m_StartTop == lua_gettop(m_LuaState));
41  }
42 
43 
44  private:
45 
46  lua_State* m_LuaState; ///< The Lua state we're checking the stack for.
47  const int m_StartTop; ///< The initial size of the stack.
48  };
49 
50 
51  /// This class implements and encapsulates the strategy with which we bind C++ objects to Lua.
52  ///
53  /// It is separate from class UniScriptStateT, because it can also be used "outside" of script states,
54  /// e.g. in the CFunctions of the bound C++ classes. Moreover, it would be possible to derive from
55  /// this class in order to implement alternative binding strategies, and to pass a concrete instance
56  /// to the UniScriptStateT constructor in order to "configure" it for a particular strategy.
57  ///
58  /// Key idea: we only pass by value!
59  /// - object is copied
60  /// - Lua "owns" it and thus determines its lifetime
61  /// - per default, always create new userdata
62  /// - simple and clear if the object has no identity: push an xyz-vector twice --> two different userdata
63  /// - if the object is a smart pointer, and has identity (entities or windows):
64  /// it would work, too, with one detail issue: per-object extensions added by Lua code (e.g. attributes or callbacks) would not be consistent:
65  /// one userdata representing the identity would have them, but not another
66  /// ==> no problem, treat smart pointers as special case
67  ///
68  /// Each type(name) can only be used with *one* kind of binding:
69  /// MyClassT A();
70  /// IntrusivePtrT<MyClassT> B(new MyClassT());
71  ///
72  /// Push(A);
73  /// Push(B); // This is not possible!
74  /// This is because we can only have one __gc function per type: for this typename, would we call
75  /// Destruct<T>(...) with T == MyClassT or with T == IntrusivePtrT<MyClassT> ?
76  /// and because the type's CFunction callbacks (for example, BaseEntityT::GetOrigin()) would similary not know with which T to call GetCheckedObjectParam<T>().
77  ///
78  /// Literature:
79  /// - Programming in Lua, 2nd edition, Roberto Ierusalimschy
80  /// - Game Programming Gems 6, chapter 4.2, "Binding C/C++ objects to Lua", W. Celes et al.
81  /// - 2008-04-01: http://thread.gmane.org/gmane.comp.lang.lua.general/46787
83  {
84  public:
85 
86  /// The constructor.
87  ScriptBinderT(lua_State* LuaState);
88 
89  /// This method registers all C++ classes known to the TIM in the related Lua state.
90  ///
91  /// Subsequently created Lua instances of these classes will use the related information
92  /// so that script code can call the C++-implemented methods, e.g. "obj:myCppFunc()".
93  /// The method also takes inheritance into account: Lua instances of derived classes can
94  /// access the attributes and call the methods of their base classes.
95  void Init(const cf::TypeSys::TypeInfoManT& TIM);
96 
97  /// Pushes the given C++ object onto the stack.
98  /// The object must support the GetType() method (should we add a "const char* TypeName" parameter instead?).
99  template<class T> bool Push(T Object);
100 
101  /// Checks if the value at the given stack index is a Lua object of type T::TypeInfo,
102  /// or a subclass derived from it, and returns a reference to the related userdata.
103  /// (If T is really an IntrusivePtrT<U>, the method checks for type U::TypeInfo,
104  /// or a subclass derived from it.)
105  template<class T> T& GetCheckedObjectParam(int StackIndex);
106 
107  /// Returns if the object with the given identity is currently bound to the Lua state,
108  /// i.e. whether for the C++ object there is an alter ego in Lua.
109  bool IsBound(void* Identity);
110 
111  /// Breaks the connection between a C++ object and its alter ego in Lua.
112  /// If the object with the given identity still has an alter ego in the Lua state,
113  /// calling this method essentially removes all C++ parts from it: the metatable is
114  /// reset to nil, and the userdata's destructor is called (Lua will collect it later).
115  /// After this method, any attempt to access the C++-implemented methods in Lua
116  /// yields a (safe and well-defined) error message.
117  void Disconnect(void* Identity);
118 
119 
120  private:
121 
122  /// Use traits for obtaining information from objects of any given type T.
123  /// If we have an instance of T, call GetType() instead, which returns the proper type
124  /// even if T is a base class pointer.
125  /// See http://erdani.com/publications/traits.html for a nice intro to traits.
126  template<class T> class TraitsT
127  {
128  public:
129 
130  static T* GetIdentity(T& Object) { return &Object; }
131  static const cf::TypeSys::TypeInfoT& GetTypeInfo() { return T::TypeInfo; }
132  static const cf::TypeSys::TypeInfoT& GetTypeInfo(const T& Object) { return *Object.GetType(); }
133  static bool IsRefCounted() { return false; }
134  };
135 
136  /// Specialization of TraitsT for IntrusivePtrTs to T.
137  template<class T> class TraitsT< IntrusivePtrT<T> >
138  {
139  public:
140 
141  static T* GetIdentity(IntrusivePtrT<T> Object) { return Object.get(); }
142  static const cf::TypeSys::TypeInfoT& GetTypeInfo() { return T::TypeInfo; }
143  static const cf::TypeSys::TypeInfoT& GetTypeInfo(IntrusivePtrT<T> Object) { return *Object->GetType(); }
144  static bool IsRefCounted() { return true; }
145  };
146 
147  friend class UniScriptStateT;
148 
149  /// Implements the one-time initialization of the Lua state for this binder.
150  /// Called by the UniScriptStateT constructor.
151  void InitState();
152 
153  /// If the object at the given stack index is an IntrusivePtrT, this method anchors it in a separate table
154  /// so that it cannot be garbage collected in Lua while it has siblings in C++.
155  ///
156  /// The method first checks if the object at the given stack index is an IntrusivePtrT.
157  /// Its reference count is expected to be at least 2 if the IntrusivePtrT was just passed in from C++ code
158  /// and a copy of it was bound to Lua, or at least 1 if Lua has the only instance (that however might soon
159  /// be returned to C++ code, where it can be copied and kept, thereby increasing the reference count).
160  /// In any case, if the object is an IntrusivePtrT, it is added to the REGISTRY.__has_ref_in_cpp set.
161  ///
162  /// This prevents the object, when it becomes (otherwise) unused in Lua, from being garbage collected.
163  /// It would normally not be a problem at all for an IntrusivePtrT object being collected, and it would be
164  /// perfectly possible to push another copy of an IntrusivePtrT to the same C++ object later.
165  ///
166  /// The only downside with garbage collecting IntrusivePtrT's that are still referenced in C++ is that any
167  /// custom Lua data that is attached to it, such as event callback functions, gets lost.
168  /// See http://thread.gmane.org/gmane.comp.lang.lua.general/92550 for details.
169  ///
170  /// Addressing this problem is in fact the sole reason for this method.
171  /// See CheckCppRefs() for the complementary method that un-anchors the objects again.
172  void Anchor(int StackIndex);
173 
174  /// This method un-anchors IntrusivePtrT objects that no longer have any siblings in C++.
175  ///
176  /// For such IntrusivePtrT's, the Lua instance is the only remaining reference to the C++ object.
177  /// Removing such objects from the REGISTRY.__has_ref_in_cpp set ensures that they can normally be
178  /// garbage collected as soon as they become unused in Lua as well.
179  ///
180  /// The user must call this method periodically (typically every n-th game frame).
181  ///
182  /// In summary, the key idea of the whole anchoring process is:
183  /// 1) When an object is newly pushed, anchor it.
184  /// 2) Every now and then, run our own "pseudo garbage collection":
185  /// a) When the object becomes unused in C++, un-anchor it.
186  /// b) If the object is passed back to C++ again, re-anchor it.
187  ///
188  /// See Anchor() for the complementary method that (re-)anchors IntrusivePtrT objects.
189  void CheckCppRefs();
190 
191  /// If i is a negative stack index (relative to the top), returns the related absolute index.
192  int abs_index(int i) const
193  {
194  return (i > 0 || i <= LUA_REGISTRYINDEX) ? i : lua_gettop(m_LuaState) + i + 1;
195  }
196 
197  /// An extra object method for objects of type IntrusivePtrT<X>.
198  template<class T> static int GetRefCount(lua_State* LuaState);
199 
200  /// The callback for the __gc metamethod.
201  template<class T> static int Destruct(lua_State* LuaState);
202 
203  lua_State* m_LuaState; ///< The Lua state that this binder is used with.
204  };
205 
206 
207  /// This class represents the state of a script:
208  /// the underlying Lua state, pending coroutines, metatables for C++ class hierarchies, etc.
209  ///
210  /// Its main features are:
211  /// - easy calling of Lua chunks, functions and object methods from C++ (Run() and Call()),
212  /// - easy to use support for coroutines/threads: Lua code can call "thread()" and "wait()",
213  /// - easy creation of Lua instances for C++ objects and binding of C++-implemented methods.
215  {
216  public:
217 
218  /// The constructor.
219  UniScriptStateT();
220 
221  /// The destructor.
223 
224  /// Loads the given string as a Lua chunk, then runs it.
225  /// (This acts very much like the stand-alone Lua interpreter.)
226  bool DoString(const char* s, const char* Signature = "", ...);
227 
228  /// Loads the given file as a Lua chunk, then runs it.
229  bool DoFile(const char* FileName, const char* Signature = "", ...);
230 
231  /// Calls the global script function with the given name.
232  ///
233  /// @param FuncName The name of the global script function to be called.
234  /// @param Signature Describes the arguments to and results from the Lua function. See below for more details.
235  /// @param ... The arguments to the Lua function and the variables that receive its results as described by the Signature parameter.
236  ///
237  /// The Signature parameter is a string of individual letters, where each letter represents a variable and its type.
238  /// The letters 'b' for bool, 'i' for int, 'f' for float, 'd' for double and 's' for const char* (string) can be used.
239  /// A '>' separates the arguments from the results, and is optional if the function returns no results.
240  /// For the results, additionally to the other letters, 'S' can be used for (address of) std::string.
241  ///
242  /// @returns whether the function call was successful.
243  /// Note that when a signature was provided that expects one or more return values and the called script code yields
244  /// (calls coroutine.yield()), the returned values are undefined and thus the call is considered a failure and false is returned.
245  /// Nonetheless, the related Lua thread is added to the list of pending coroutines for later resumption.
246  bool Call(const char* FuncName, const char* Signature="", ...);
247 
248  /// Calls a method with the given name of the given object.
249  ///
250  /// @param Object The object whose script method is to be called.
251  /// @param MethodName The name of the method to be called.
252  /// @param Signature Describes the arguments to and results from the Lua method.
253  /// @param ... The arguments to the Lua method and the variables that receive its results as described by the Signature parameter.
254  ///
255  /// For more details about the parameters and return value, see Call().
256  ///
257  /// Example:
258  /// If the variable Obj is bound to Object, then CallMethod(Object, "OnTrigger", "f", 1.0);
259  /// calls the script method Obj:OnTrigger(value) where value is a number with value 1.0.
260  template<class T> bool CallMethod(T Object, const std::string& MethodName, const char* Signature="", ...);
261 
262  /// Like CallMethod() above, but the arguments and results are passed via vl rather than "...",
263  /// and if any extra arguments have been pushed on the stack, their number must be given.
264  /// Note that the "extra arguments" are a work-around that was not necessary if we could use
265  /// variadic templates for the implementation of CallMethod().
266  template<class T> bool CallMethod_Impl(T Object, const std::string& MethodName, int NumExtraArgs, const char* Signature, va_list vl);
267 
268  /// Runs the pending coroutines.
269  void RunPendingCoroutines(float FrameTime);
270 
271  /// Returns the Lua state that implements this script state.
272  lua_State* GetLuaState() { return m_LuaState; }
273 
274 
275  private:
276 
277  class CoroutineT
278  {
279  public:
280 
281  CoroutineT();
282 
283  unsigned int ID; ///< The unique ID of this coroutine, used to anchor it in a table in the Lua registry. Automatically set in the constructor, but not declared const so that CoroutineT objects can be kept in an ArrayT<>.
284  lua_State* State; ///< The state and stack of this coroutine.
285  unsigned int NumParams; ///< Number of parameters on the stack of State for the next call to lua_resume(), i.e. the parameters for the initial function call or the return values for the pending yield().
286  float WaitTimeLeft; ///< Wait time left until the next call to lua_resume().
287 
288 
289  private:
290 
291  static unsigned int InstCount; ///< Count of created instances, used for creating unique coroutine IDs.
292  };
293 
294  UniScriptStateT(const UniScriptStateT&); ///< Use of the Copy Constructor is not allowed.
295  void operator = (const UniScriptStateT&); ///< Use of the Assignment Operator is not allowed.
296 
297  /// This method calls a Lua function in the context of the Lua state.
298  bool StartNewCoroutine(int NumExtraArgs, const char* Signature, va_list vl, const std::string& DbgName);
299 
300  /// A global Lua function that registers the given Lua function as a new thread.
301  static int RegisterThread(lua_State* LuaState);
302 
303  /// A helper function for checking if a called Lua function is documented.
304  static void CheckCallbackDoc(const cf::TypeSys::TypeInfoT* TI, const std::string& MethodName, int NumExtraArgs, const char* Signature);
305 
306  lua_State* m_LuaState; ///< The Lua instance. This is what "really" represents the script.
307  ArrayT<CoroutineT> m_PendingCoroutines; ///< The list of active, pending coroutines.
308  unsigned int m_CheckCppRefsCount; ///< Call Binder.CheckCppRefs() only every n-th frame.
309  };
310 }
311 
312 
313 template<class T> int cf::ScriptBinderT::GetRefCount(lua_State* LuaState)
314 {
315  // Cannot use Binder.GetCheckedObjectParam() here, because it would cause infinite
316  // recursion (via the call to Anchor()).
317  lua_getfield(LuaState, -1, "__userdata_cf");
318 
319  // Note that T is an IntrusivePtrT<X>.
320  T* UserData=(T*)lua_touserdata(LuaState, -1);
321 
322  assert(UserData);
323  lua_pushinteger(LuaState, UserData ? (*UserData)->GetRefCount() : 0);
324  return 1;
325 }
326 
327 
328 template<class T> int cf::ScriptBinderT::Destruct(lua_State* LuaState)
329 {
330  T* UserData=(T*)lua_touserdata(LuaState, 1);
331 
332  if (UserData)
333  {
334  // Explicitly call the destructor for the placed object.
335  UserData->~T();
336  }
337 
338  return 0;
339 }
340 
341 
342 template<class T> inline bool cf::ScriptBinderT::Push(T Object)
343 {
344  const StackCheckerT StackChecker(m_LuaState, 1);
345 
346  // Put the REGISTRY["__identity_to_object"] table onto the stack.
347  lua_getfield(m_LuaState, LUA_REGISTRYINDEX, "__identity_to_object");
348 
349  // Put __identity_to_object[Identity] onto the stack.
350  // This should be our table that represents the object.
351  lua_pushlightuserdata(m_LuaState, TraitsT<T>::GetIdentity(Object)); // Need the raw "identity" pointer here.
352  lua_rawget(m_LuaState, -2);
353 
354  // If the object was not found in __identity_to_object, create it anew.
355  if (lua_isnil(m_LuaState, -1))
356  {
357  // Remove the nil.
358  lua_pop(m_LuaState, 1);
359 
360  // Stack indices of the table and userdata that we process here.
361  const int USERDATA_INDEX=lua_gettop(m_LuaState) + 2;
362  const int TABLE_INDEX =lua_gettop(m_LuaState) + 1;
363 
364  // Create a new object table OT, which is pushed on the stack and thus at stack index TABLE_INDEX.
365  lua_newtable(m_LuaState);
366 
367  // Create a new user datum UD, which is pushed on the stack and thus at stack index USERDATA_INDEX.
368  new (lua_newuserdata(m_LuaState, sizeof(T))) T(Object);
369 
370  // OT["__userdata_cf"] = UD
371  lua_pushvalue(m_LuaState, USERDATA_INDEX); // Duplicate the userdata on top of the stack.
372  lua_setfield(m_LuaState, TABLE_INDEX, "__userdata_cf");
373 
374  // Get the table with name (key) TraitsT<T>::GetTypeInfo(Object).ClassName from the registry,
375  // and check if its __gc metamethod is already set.
376  // Note that starting with Lua 5.2, the __gc field must be set *before* the table is set as metatable (below),
377  // or else the finalizer will not be called even if it is set later (see ยง2.5.1 in the Lua Reference Manual).
378  luaL_getmetatable(m_LuaState, TraitsT<T>::GetTypeInfo(Object).ClassName);
379  assert(lua_istable(m_LuaState, -1));
380  lua_getfield(m_LuaState, -1, "__gc");
381  if (lua_isnil(m_LuaState, -1))
382  {
383  lua_pushcfunction(m_LuaState, Destruct<T>);
384  lua_setfield(m_LuaState, -3, "__gc");
385  }
386  lua_pop(m_LuaState, 2);
387 
388  // Get the table with name TraitsT<T>::GetTypeInfo(Object).ClassName from the registry,
389  // and set it as metatable of the newly created table.
390  // This is the crucial step that establishes the main functionality of our new table.
391  luaL_getmetatable(m_LuaState, TraitsT<T>::GetTypeInfo(Object).ClassName);
392  assert(lua_istable(m_LuaState, -1));
393  lua_setmetatable(m_LuaState, TABLE_INDEX);
394 
395  // Get the table with name (key) TraitsT<T>::GetTypeInfo(Object).ClassName from the registry,
396  // and set it as metatable of the newly created userdata item.
397  // This is important for userdata type safety (see PiL2, chapter 28.2) and to have automatic garbage collection work
398  // (contrary to the text in the "Game Programming Gems 6" book, chapter 4.2, a __gc method in the metatable
399  // is only called for full userdata, see my email to the Lua mailing list on 2008-Apr-01 for more details).
400  luaL_getmetatable(m_LuaState, TraitsT<T>::GetTypeInfo(Object).ClassName);
401  assert(lua_istable(m_LuaState, -1));
402  lua_setmetatable(m_LuaState, USERDATA_INDEX);
403 
404  // Get the table for the root of TraitsT<T>::GetTypeInfo(Object) from the registry,
405  // get its __index table, and check if its GetRefCount method is already set.
406  if (TraitsT<T>::IsRefCounted())
407  {
408  const cf::TypeSys::TypeInfoT* TI = &TraitsT<T>::GetTypeInfo(Object);
409 
410  while (TI->Base)
411  TI = TI->Base;
412 
413  luaL_getmetatable(m_LuaState, TI->ClassName);
414  lua_getfield(m_LuaState, -1, "__index");
415  lua_getfield(m_LuaState, -1, "GetRefCount");
416  if (lua_isnil(m_LuaState, -1))
417  {
418  lua_pushcfunction(m_LuaState, GetRefCount<T>);
419  lua_setfield(m_LuaState, -3, "GetRefCount");
420  }
421  lua_pop(m_LuaState, 3);
422  }
423 
424  // Remove UD from the stack, so that now the new table OT is on top of the stack.
425  lua_pop(m_LuaState, 1);
426 
427  // Record the table: __identity_to_object[Identity] = OT
428  lua_pushlightuserdata(m_LuaState, TraitsT<T>::GetIdentity(Object)); // Need the raw "identity" pointer here.
429  lua_pushvalue(m_LuaState, TABLE_INDEX); // Duplicate the table on top of the stack.
430  lua_rawset(m_LuaState, -4);
431 
432  // Anchor the object (table OT).
433  // Note that this is not necessary if the object was found in __identity_to_object above,
434  // because then, clearly a copy in C++ and a copy in Lua existed beforehand, so that
435  // consequently the object must also be anchored.
436  Anchor(TABLE_INDEX);
437  }
438 
439  // Remove the __identity_to_object table.
440  lua_remove(m_LuaState, -2);
441 
442  // The requested table/userdata is now at the top of the stack.
443  return true;
444 }
445 
446 
447 template<class T> inline T& cf::ScriptBinderT::GetCheckedObjectParam(int StackIndex)
448 {
449  // Don't bother with stack checking, because here it can only work in the case of success.
450  // If there is an error, not only do we not clean up the stack, but the error message itself is
451  // a problem as well: See http://thread.gmane.org/gmane.comp.lang.lua.general/103390 for details.
452  // const StackCheckerT StackChecker(m_LuaState);
453 
454  StackIndex = abs_index(StackIndex);
455 
456  // First make sure that the table that represents the object itself is at StackIndex.
457  luaL_argcheck(m_LuaState, lua_istable(m_LuaState, StackIndex), StackIndex, "Expected a table that represents an object." /*of type TypeInfo.ClassName*/);
458 
459  // Put the contents of the "__userdata_cf" field on top of the stack (other values may be between it and the table at position StackIndex).
460  lua_getfield(m_LuaState, StackIndex, "__userdata_cf");
461 
462 #if 1
463  // This approach takes inheritance properly into account by "manually traversing up the inheritance hierarchy".
464  // See the "Game Programming Gems 6" book, page 353 for the inspiration for this code.
465 
466  // Put the metatable of the desired type on top of the stack.
467  luaL_getmetatable(m_LuaState, TraitsT<T>::GetTypeInfo().ClassName);
468 
469  // Put the metatable for the given userdata on top of the stack (it may belong to a derived class).
470  if (!lua_getmetatable(m_LuaState, -2)) lua_pushnil(m_LuaState); // Don't have it push nothing in case of failure.
471 
472  while (lua_istable(m_LuaState, -1))
473  {
474  if (lua_rawequal(m_LuaState, -1, -2))
475  {
476  T* UserData=(T*)lua_touserdata(m_LuaState, -3);
477 
478  if (UserData==NULL)
479  luaL_error(m_LuaState, "NULL userdata in object table.");
480 
481  // Pop the two matching metatables and the userdata.
482  lua_pop(m_LuaState, 3);
483 
484  // We pass the object back to C++, fully expecting that it will keep a copy
485  // and, if it is an IntrusivePtrT, increase its reference count.
486  Anchor(StackIndex);
487 
488  return *UserData;
489  }
490 
491  // Replace the metatable MT on top of the stack with the metatable of MT.__index.
492  lua_getfield(m_LuaState, -1, "__index");
493  if (!lua_getmetatable(m_LuaState, -1)) lua_pushnil(m_LuaState); // Don't have it push nothing in case of failure.
494  lua_remove(m_LuaState, -2);
495  lua_remove(m_LuaState, -2);
496  }
497 
498  // luaL_typerror(m_LuaState, StackIndex, TraitsT<T>::GetTypeInfo().ClassName);
499  luaL_argerror(m_LuaState, StackIndex, lua_pushfstring(m_LuaState, "%s expected, got %s", TraitsT<T>::GetTypeInfo().ClassName, luaL_typename(m_LuaState, StackIndex)));
500 
501  static T* Invalid = NULL;
502  return *Invalid;
503 #else
504  // This approach is too simplistic, it doesn't work when inheritance is used.
505  T* UserData=(T*)luaL_checkudata(m_LuaState, -1, TraitsT<T>::GetTypeInfo().ClassName);
506 
507  if (UserData==NULL)
508  luaL_error(m_LuaState, "NULL userdata in object table.");
509 
510  // Pop the userdata from the stack again. Not necessary though as it doesn't hurt there.
511  // lua_pop(m_LuaState, 1);
512  return *UserData;
513 #endif
514 }
515 
516 
517 template<class T> inline bool cf::UniScriptStateT::CallMethod(T Object, const std::string& MethodName, const char* Signature, ...)
518 {
519  va_list vl;
520 
521  va_start(vl, Signature);
522  const bool Result=CallMethod_Impl(Object, MethodName, 0, Signature, vl);
523  va_end(vl);
524 
525  return Result;
526 }
527 
528 
529 template<class T> inline bool cf::UniScriptStateT::CallMethod_Impl(T Object, const std::string& MethodName, int NumExtraArgs, const char* Signature, va_list vl)
530 {
531  const StackCheckerT StackChecker(m_LuaState, -NumExtraArgs);
532  cf::ScriptBinderT Binder(m_LuaState);
533 
534 #ifdef DEBUG
535  CheckCallbackDoc(Object->GetType(), MethodName, NumExtraArgs, Signature);
536 #endif
537 
538  Binder.Push(Object);
539 
540  // Put the desired method (directly from the object's table or
541  // from its metatables __index table) onto the stack of LuaState.
542  lua_getfield(m_LuaState, -1, MethodName.c_str());
543 
544  if (!lua_isfunction(m_LuaState, -1))
545  {
546  // If we get here, this usually means that the value at -1 is just nil, i.e. the
547  // function that we would like to call was just not defined in the Lua script.
548  // Pop whatever is not a function, the object table, and any extra arguments.
549  lua_pop(m_LuaState, 2 + NumExtraArgs);
550  return false;
551  }
552 
553  // Rearrange the stack from "method, Object, extra arguments" to "extra arguments, Object, method",
554  // so that the function call sees the Object as its first argument (the "self" or "this" value for
555  // the object-oriented method call), followed by any extra arguments.
556  lua_insert(m_LuaState, -2 - NumExtraArgs); // Move the method below the Object and the extra args.
557  lua_insert(m_LuaState, -1 - NumExtraArgs); // Move the Object below the extra args.
558 
559  // The stack is now prepared as required by the StartNewCoroutine() method.
560  return StartNewCoroutine(1 + NumExtraArgs, Signature, vl, std::string("method ") + MethodName + "()");
561 }
562 
563 #endif
bool IsBound(void *Identity)
Returns if the object with the given identity is currently bound to the Lua state, i.e.
Definition: UniScriptState.cpp:214
void RunPendingCoroutines(float FrameTime)
Runs the pending coroutines.
Definition: UniScriptState.cpp:458
UniScriptStateT()
The constructor.
Definition: UniScriptState.cpp:312
This class implements smart (reference-counted) pointers.
Definition: Pointer.hpp:43
void Init(const cf::TypeSys::TypeInfoManT &TIM)
This method registers all C++ classes known to the TIM in the related Lua state.
Definition: UniScriptState.cpp:158
bool CallMethod(T Object, const std::string &MethodName, const char *Signature="",...)
Calls a method with the given name of the given object.
Definition: UniScriptState.hpp:517
bool DoString(const char *s, const char *Signature="",...)
Loads the given string as a Lua chunk, then runs it.
Definition: UniScriptState.cpp:379
ScriptBinderT(lua_State *LuaState)
The constructor.
Definition: UniScriptState.cpp:28
lua_State * GetLuaState()
Returns the Lua state that implements this script state.
Definition: UniScriptState.hpp:272
T & GetCheckedObjectParam(int StackIndex)
Checks if the value at the given stack index is a Lua object of type T::TypeInfo, or a subclass deriv...
Definition: UniScriptState.hpp:447
This class manages the type infos.
Definition: TypeSys.hpp:145
~UniScriptStateT()
The destructor.
Definition: UniScriptState.cpp:362
This class checks if the Lua stack has the same size at the start and the end of a function...
Definition: UniScriptState.hpp:28
T * get() const
Returns the stored pointer.
Definition: Pointer.hpp:99
This class implements and encapsulates the strategy with which we bind C++ objects to Lua...
Definition: UniScriptState.hpp:82
bool Push(T Object)
Pushes the given C++ object onto the stack.
Definition: UniScriptState.hpp:342
bool DoFile(const char *FileName, const char *Signature="",...)
Loads the given file as a Lua chunk, then runs it.
Definition: UniScriptState.cpp:403
void Disconnect(void *Identity)
Breaks the connection between a C++ object and its alter ego in Lua.
Definition: UniScriptState.cpp:237
const char * ClassName
The name of this class.
Definition: TypeSys.hpp:109
bool CallMethod_Impl(T Object, const std::string &MethodName, int NumExtraArgs, const char *Signature, va_list vl)
Like CallMethod() above, but the arguments and results are passed via vl rather than "...
Definition: UniScriptState.hpp:529
This class represents the state of a script: the underlying Lua state, pending coroutines, metatables for C++ class hierarchies, etc.
Definition: UniScriptState.hpp:214
This class keeps type information (about an entity class that occurs in the game).
Definition: TypeSys.hpp:79
const TypeInfoT * Base
The type info for the base class.
Definition: TypeSys.hpp:114
bool Call(const char *FuncName, const char *Signature="",...)
Calls the global script function with the given name.
Definition: UniScriptState.cpp:429