Pioneer
LuaObject.h
Go to the documentation of this file.
1 // Copyright © 2008-2021 Pioneer Developers. See AUTHORS.txt for details
2 // Licensed under the terms of the GPL v3. See licenses/GPL-3.txt
3 
4 #ifndef _LUAOBJECT_H
5 #define _LUAOBJECT_H
6 
7 #include "DeleteEmitter.h"
8 #include "Lua.h"
9 #include "LuaPushPull.h"
10 #include "LuaRef.h"
11 #include "LuaUtils.h"
12 #include "LuaWrappable.h"
13 #include "RefCounted.h"
14 #include "galaxy/SystemPath.h"
15 #include <tuple>
16 #include <typeinfo>
17 
18 //
19 // LuaObject provides proxy objects and tracking facilities to safely get
20 // objects in and out of lua. the basic idea is that for every class you want
21 // to expose to lua, you define LuaObject wrapper class that defines the
22 // lua name lua name, methods and metamethods for that class. you then call
23 // methods on this class to push and pull objects to and from the lua stack
24 //
25 // Push an object to the Lua stack:
26 //
27 // // C++-owned, still responsible for deletion
28 // Ship *s = new Ship("wave");
29 // LuaObject<Ship>::PushToLua(s);
30 //
31 // // RefCounted, Lua will take a reference
32 // StarSystem *s = Pi::GetGalaxy()->GetStarSystem(SystemPath(0,0,0,0));
33 // LuaObject<StarSystem>::PushToLua(s);
34 //
35 // // Stack-allocated, Lua will get a copy
36 // SystemPath path(0,0,0,0,1);
37 // LuaObject<SystemPath>::PushToLua(path);
38 //
39 // // Create an object, fully owned by lua
40 // // WARNING! the lifetime of an object will be determined by the lua garbage
41 // // collector, so a pointer to it should not be stored in any form on the C++ side
42 // LuaObject<Ship>::CreateInLua(ship_id); // constructor arguments are passed
43 //
44 // Get an object from the Lua stack at index n. Causes a Lua exception if the
45 // object doesn't exist or the types don't match.
46 //
47 // Ship *s = LuaObject<Ship>::CheckFromLua(1);
48 //
49 // Or alternatively, get it and return 0 on failure.
50 //
51 // Ship *s = LuaObject<Ship>::GetFromLua(1);
52 //
53 //
54 // If you need to expose a new class to Lua:
55 //
56 // - Have it inherit from LuaWrappable
57 //
58 // - Have it either:
59 // - inherit from DeleteEmitter
60 // - inherit from RefCounted
61 // - implement a copy constructor
62 //
63 // - Arrange for the wrapper class RegisterClass() method to be called in
64 // LuaInit in Pi.cpp
65 //
66 // - Make a new file LuaWhatever.cpp implement RegisterClass() and any
67 // methods, metamethods and attribute methods you want. Copy from one of the
68 // other files to get the idea
69 //
70 // - Add the new file to the build system
71 //
72 
73 // type for promotion test callbacks
74 typedef bool (*PromotionTest)(LuaWrappable *o);
75 
76 // type for serializer function pair
78  typedef std::string (*Serializer)(LuaWrappable *o);
79  typedef bool (*Deserializer)(const char *stream, const char **next);
80 
81  typedef void (*ToJson)(Json &out, LuaWrappable *o);
82  typedef bool (*FromJson)(const Json &obj);
83 
85  serialize(nullptr),
86  deserialize(nullptr),
87  to_json(nullptr),
88  from_json(nullptr)
89  {}
90 
92  Serializer serialize_, Deserializer deserialize_,
93  ToJson to_json_, FromJson from_json_) :
94  serialize(serialize_),
95  deserialize(deserialize_),
96  to_json(to_json_),
97  from_json(from_json_)
98  {}
99 
104 };
105 
106 class PropertyMap;
107 class LuaMetaTypeBase;
108 
109 // wrapper baseclass, and extra bits for getting at certain parts of the
110 // LuaObject layer
112  friend class LuaSerializer;
113 
114 public:
115  // creates a single "typeless" object and attaches the listed methods,
116  // attributes and metamethods to it. leaves the created object on the
117  // stack
118  static void CreateObject(const luaL_Reg *methods, const luaL_Reg *attrs, const luaL_Reg *meta, bool protect = false);
119 
120  // Creates a single "typeless" object and attaches the given metatype
121  // to it. Leaves the created object on the stack.
122  static void CreateObject(LuaMetaTypeBase *metaType);
123 
124  // Checks if the object at the stack position is a PropertiedObject and returns a pointer to
125  // its PropertyMap. Returns nullptr on failure.
126  static PropertyMap *GetPropertiesFromObject(lua_State *l, int object);
127 
128 protected:
129  // base class constructor, called by the wrapper Push* methods
130  LuaObjectBase(const char *type) :
131  m_type(type){};
132  virtual ~LuaObjectBase() {}
133 
134  // creates a class in the lua vm with the given name and attaches the
135  // listed methods to it and the listed metamethods to its metaclass. if
136  // attributes extra magic is added to the metaclass to make them work as
137  // expected
138  static void CreateClass(const char *type, const char *parent, const luaL_Reg *methods, const luaL_Reg *attrs, const luaL_Reg *meta);
139 
140  // Creates a class in the lua vm with the given name and attaches the
141  // listed metatype to it. All method, attribute, and metamethod handling
142  // is contained in the metatype.
143  static void CreateClass(LuaMetaTypeBase *metaType);
144 
145  // push an already-registered object onto the lua stack. the object is
146  // looked up in the lua registry, if it exists its userdata its userdata
147  // is pushed onto the lua stack. returns true if the object exists and was
148  // pushed, false otherwise
149  static bool PushRegistered(LuaWrappable *o);
150 
151  // adds an object->wrapper mapping to the registry for the given wrapper
152  // object. the wrapper's corresponding userdata should be on the top of
153  // the stack
154  static void Register(LuaObjectBase *lo);
155 
156  // remove the object->wrapper from the registry. checks to make sure the
157  // the mapping matches first, to protect against memory being reused
158  static void Deregister(LuaObjectBase *lo);
159 
160  // pulls an object off the lua stack and returns its associated c++
161  // object. type is the lua type string of the object. a lua exception is
162  // triggered if the object on the stack is not of this type
163  static LuaWrappable *CheckFromLua(int index, const char *type);
164 
165  // does exactly the same as Check without triggering exceptions
166  static LuaWrappable *GetFromLua(int index, const char *type);
167 
168  // register a promotion test. when an object with lua type base_type is
169  // pushed, test_fn will be called. if it returns true then the created lua
170  // object will be of target_type
171  static void RegisterPromotion(const char *base_type, const char *target_type, PromotionTest test_fn);
172 
173  static void RegisterSerializer(const char *type, SerializerPair pair);
174 
175  std::string Serialize();
176  static bool Deserialize(const char *stream, const char **next);
177 
178  void ToJson(Json &out);
179  static bool FromJson(const Json &obj);
180 
181  // allocate n bytes from Lua memory and leave it an associated userdata on
182  // the stack. this is a wrapper around lua_newuserdata
183  static void *Allocate(size_t n);
184 
185  // get a pointer to the underlying object
186  virtual LuaWrappable *GetObject() const = 0;
187 
188  const char *GetType() const { return m_type; }
189 
190 private:
191  LuaObjectBase() {}
192  LuaObjectBase(const LuaObjectBase &) {}
193 
194  // Lua-side helper functionality, declared in LuaObject.cpp
195  friend class LuaObjectHelpers;
196 
197  // determine if the object has a class in its ancestry
198  bool Isa(const char *base) const;
199 
200  // lua type (ie method/metatable name)
201  const char *m_type;
202 };
203 
204 // templated portion of the wrapper baseclass
205 template <typename T>
206 class LuaObject : public LuaObjectBase {
207 public:
208  // registers the class with the lua vm
209  static void RegisterClass();
210 
211  // wrap an object and push it onto the stack. these create a wrapper
212  // object that knows how to deal with the type of object
213  static inline void PushToLua(DeleteEmitter *o); // LuaCoreObject
214  static inline void PushToLua(RefCounted *o); // LuaSharedObject
215  static inline void PushToLua(const T &o); // LuaCopyObject
216  template <typename... Args>
217  static inline void CreateInLua(Args &&... args);
218 
219  template <typename Ret, typename Key, typename... Args>
220  static inline Ret CallMethod(T *o, const Key &key, const Args &... args);
221  template <typename Key, typename... Args>
222  static inline void CallMethod(T *o, const Key &key, const Args &... args)
223  {
224  CallMethod<bool>(o, key, args...);
225  }
226 
227  template <typename Ret1, typename Ret2, typename... Ret, typename Key, typename... Args>
228  static inline std::tuple<Ret1, Ret2, Ret...> CallMethod(T *o, const Key &key, const Args &... args);
229 
230  // pull an object off the stack, unwrap and return it
231  // if not found or doesn't match the type, throws a lua exception
232  static inline T *CheckFromLua(int idx)
233  {
234 #ifdef __clang__
235 #pragma clang diagnostic push
236 #pragma clang diagnostic ignored "-Wundefined-var-template"
237 #endif
238  return dynamic_cast<T *>(LuaObjectBase::CheckFromLua(idx, s_type));
239 #ifdef __clang__
240 #pragma clang diagnostic pop
241 #endif
242  }
243 
244  // same but without error checks. returns 0 on failure
245  static inline T *GetFromLua(int idx)
246  {
247 #ifdef __clang__
248 #pragma clang diagnostic push
249 #pragma clang diagnostic ignored "-Wundefined-var-template"
250 #endif
251  return dynamic_cast<T *>(LuaObjectBase::GetFromLua(idx, s_type));
252 #ifdef __clang__
253 #pragma clang diagnostic pop
254 #endif
255  }
256 
257  // standard cast promotion test for convenience
258  static inline bool DynamicCastPromotionTest(LuaWrappable *o)
259  {
260  return dynamic_cast<T *>(o);
261  }
262 
263 protected:
264 #ifdef __clang__
265 #pragma clang diagnostic push
266 #pragma clang diagnostic ignored "-Wundefined-var-template"
267 #endif
269  LuaObjectBase(s_type)
270  {}
271 #ifdef __clang__
272 #pragma clang diagnostic pop
273 #endif
274 
275 private:
276  // initial lua type string. defined in a specialisation in the appropriate
277  // .cpp file
278  static const char *s_type;
279 };
280 
281 // wrapper for a "core" object - one owned by c++ (eg Body).
282 // Lua needs to know when the object is deleted so that it can handle
283 // requests for it appropriately (ie with an exception, or exists())
284 template <typename T>
285 class LuaCoreObject : public LuaObject<T> {
286 public:
288  m_object(o)
289  {
290  m_deleteConnection = m_object->DeleteEmitter::onDelete.connect(sigc::mem_fun(this, &LuaCoreObject::OnDelete));
291  }
292 
294  {
295  if (m_deleteConnection.connected())
296  m_deleteConnection.disconnect();
297  }
298 
300  {
301  return m_object;
302  }
303 
304 private:
305  void OnDelete()
306  {
308  m_object = 0;
309  }
310 
311  T *m_object;
312  sigc::connection m_deleteConnection;
313 };
314 
315 // wrapper for a "shared" object - one that can comfortably exist in both
316 // environments. usually for long-lived (StarSystem) or standalone (UI
317 // widget) objects
318 // Lua simply needs to keep a reference to these
319 template <typename T>
320 class LuaSharedObject : public LuaObject<T> {
321 public:
323  m_object(o) {}
324 
326  {
327  return m_object.Get();
328  }
329 
330 private:
331  RefCountedPtr<T> m_object;
332 };
333 
334 // wrapper for a "copied" object. a new one is created via the copy
335 // constructor and fully owned by Lua. good for lightweight POD-style objects
336 // (eg SystemPath)
337 template <typename T>
338 class LuaCopyObject : public LuaObject<T> {
339 public:
340  LuaCopyObject(const T &o)
341  {
342  lua_State *l = Lua::manager->GetLuaState();
343  m_object = new (LuaObjectBase::Allocate(sizeof(T))) T(o);
344  m_ref = LuaRef(l, -1);
345  lua_pop(l, 1);
346  }
347 
349  {
350  m_object->~T();
351  m_object = 0;
352  }
353 
355  {
356  return m_object;
357  }
358 
359 private:
360  T *m_object;
361  LuaRef m_ref;
362 };
363 
364 // wrapper for a "lua-owned" object.
365 // the wrapper is deleted by lua gc, and when it is deleted, it also deletes the wrapped object
366 template <typename T>
367 class LuaOwnObject : public LuaObject<T> {
368 public:
370  {
371  m_object = o;
372  }
373 
375  {
376  delete (m_object);
377  }
378 
380  {
381  return m_object;
382  }
383 
384 private:
385  T *m_object;
386 };
387 
388 // push methods, create wrappers if necessary
389 // wrappers are allocated from Lua memory
390 template <typename T>
392 {
393  if (!PushRegistered(o))
394  Register(new (LuaObjectBase::Allocate(sizeof(LuaCoreObject<T>))) LuaCoreObject<T>(static_cast<T *>(o)));
395 }
396 
397 template <typename T>
399 {
400  if (!PushRegistered(o))
401  Register(new (LuaObjectBase::Allocate(sizeof(LuaSharedObject<T>))) LuaSharedObject<T>(static_cast<T *>(o)));
402 }
403 
404 template <typename T>
405 inline void LuaObject<T>::PushToLua(const T &o)
406 {
408 }
409 
410 template <typename T>
411 template <typename... Args>
412 inline void LuaObject<T>::CreateInLua(Args &&... args)
413 {
414  T *p(new T(std::forward<Args>(args)...));
415  Register(new (LuaObjectBase::Allocate(sizeof(LuaOwnObject<T>))) LuaOwnObject<T>(static_cast<T *>(p)));
416 }
417 
418 template <typename T>
419 template <typename Ret, typename Key, typename... Args>
420 inline Ret LuaObject<T>::CallMethod(T *o, const Key &key, const Args &... args)
421 {
422  lua_State *l = Lua::manager->GetLuaState();
423  LUA_DEBUG_START(l);
424  Ret return_value;
425 
426  lua_checkstack(l, sizeof...(args) + 5);
427  PushToLua(o);
428  pi_lua_generic_push(l, key);
429  lua_gettable(l, -2);
430  lua_pushvalue(l, -2);
431  lua_remove(l, -3);
432  pi_lua_multiple_push(l, args...);
433  pi_lua_protected_call(l, sizeof...(args) + 1, 1);
434  pi_lua_generic_pull(l, -1, return_value);
435  lua_pop(l, 1);
436  LUA_DEBUG_END(l, 0);
437  return return_value;
438 }
439 
440 template <typename T>
441 template <typename Ret1, typename Ret2, typename... Ret, typename Key, typename... Args>
442 inline std::tuple<Ret1, Ret2, Ret...> LuaObject<T>::CallMethod(T *o, const Key &key, const Args &... args)
443 {
444  lua_State *l = Lua::manager->GetLuaState();
445 
446  LUA_DEBUG_START(l);
447  lua_checkstack(l, sizeof...(args) + 5);
448  PushToLua(o);
449  pi_lua_generic_push(l, key);
450  lua_gettable(l, -2);
451  lua_pushvalue(l, -2);
452  lua_remove(l, -3);
453  pi_lua_multiple_push(l, args...);
454  pi_lua_protected_call(l, sizeof...(args) + 1, 2 + sizeof...(Ret));
455  auto ret_values = pi_lua_multiple_pull<Ret1, Ret2, Ret...>(l, -2 - static_cast<int>(sizeof...(Ret)));
456  lua_pop(l, 2 + static_cast<int>(sizeof...(Ret)));
457  LUA_DEBUG_END(l, 0);
458  return ret_values;
459 }
460 
461 // specialise for SystemPath, which needs custom machinery to deduplicate system paths
462 class SystemPath;
463 template <>
465 
466 inline void pi_lua_generic_pull(lua_State *l, int index, SystemPath &out)
467 {
468  assert(l == Lua::manager->GetLuaState());
470 }
471 
472 inline void pi_lua_generic_push(lua_State *l, const SystemPath &value)
473 {
474  assert(l == Lua::manager->GetLuaState());
476 }
477 
478 // LuaPushPull stuff.
479 template <class T>
480 void pi_lua_generic_pull(lua_State *l, int index, T *&out)
481 {
482  assert(l == Lua::manager->GetLuaState());
484 }
485 
486 template <class T>
487 bool pi_lua_strict_pull(lua_State *l, int index, T *&out)
488 {
489  assert(l == Lua::manager->GetLuaState());
491  return out != 0;
492 }
493 
494 template <class T>
495 void pi_lua_generic_push(lua_State *l, T *value)
496 {
497  assert(l == Lua::manager->GetLuaState());
498  if (value)
500  else
501  lua_pushnil(l);
502 }
503 
504 #endif
nlohmann::json Json
Definition: Json.h:8
void pi_lua_generic_pull(lua_State *l, int index, SystemPath &out)
Definition: LuaObject.h:466
bool(* PromotionTest)(LuaWrappable *o)
Definition: LuaObject.h:74
void pi_lua_generic_push(lua_State *l, const SystemPath &value)
Definition: LuaObject.h:472
bool pi_lua_strict_pull(lua_State *l, int index, T *&out)
Definition: LuaObject.h:487
std::tuple< Types... > pi_lua_multiple_pull(lua_State *l, int beg)
Definition: LuaPushPull.h:215
void pi_lua_multiple_push(lua_State *l, Types... args)
#define LUA_DEBUG_START(luaptr)
Definition: LuaUtils.h:100
#define LUA_DEBUG_END(luaptr, expectedStackDiff)
Definition: LuaUtils.h:101
void pi_lua_protected_call(lua_State *L, int nargs, int nresults)
Definition: Sandbox.cpp:172
Definition: DeleteEmitter.h:16
Definition: LuaObject.h:338
LuaCopyObject(const T &o)
Definition: LuaObject.h:340
LuaWrappable * GetObject() const
Definition: LuaObject.h:354
~LuaCopyObject()
Definition: LuaObject.h:348
Definition: LuaObject.h:285
LuaCoreObject(T *o)
Definition: LuaObject.h:287
~LuaCoreObject()
Definition: LuaObject.h:293
LuaWrappable * GetObject() const
Definition: LuaObject.h:299
lua_State * GetLuaState()
Definition: LuaManager.h:14
Definition: LuaMetaType.h:12
Definition: LuaObject.h:111
static void CreateObject(const luaL_Reg *methods, const luaL_Reg *attrs, const luaL_Reg *meta, bool protect=false)
Definition: LuaObject.cpp:288
static void * Allocate(size_t n)
Definition: LuaObject.cpp:743
const char * GetType() const
Definition: LuaObject.h:188
virtual LuaWrappable * GetObject() const =0
static bool Deserialize(const char *stream, const char **next)
Definition: LuaObject.cpp:692
static LuaWrappable * CheckFromLua(int index, const char *type)
Definition: LuaObject.cpp:589
void ToJson(Json &out)
Definition: LuaObject.cpp:713
LuaObjectBase(const char *type)
Definition: LuaObject.h:130
static PropertyMap * GetPropertiesFromObject(lua_State *l, int object)
Definition: LuaObject.cpp:239
static LuaWrappable * GetFromLua(int index, const char *type)
Definition: LuaObject.cpp:609
static void RegisterPromotion(const char *base_type, const char *target_type, PromotionTest test_fn)
Definition: LuaObject.cpp:665
static void CreateClass(const char *type, const char *parent, const luaL_Reg *methods, const luaL_Reg *attrs, const luaL_Reg *meta)
Definition: LuaObject.cpp:347
virtual ~LuaObjectBase()
Definition: LuaObject.h:132
static bool PushRegistered(LuaWrappable *o)
Definition: LuaObject.cpp:453
static void Register(LuaObjectBase *lo)
Definition: LuaObject.cpp:487
std::string Serialize()
Definition: LuaObject.cpp:675
static void RegisterSerializer(const char *type, SerializerPair pair)
Definition: LuaObject.cpp:670
static bool FromJson(const Json &obj)
Definition: LuaObject.cpp:729
static void Deregister(LuaObjectBase *lo)
Definition: LuaObject.cpp:567
Definition: LuaObject.cpp:96
Definition: LuaObject.h:206
static void CreateInLua(Args &&... args)
Definition: LuaObject.h:412
static void CallMethod(T *o, const Key &key, const Args &... args)
Definition: LuaObject.h:222
static void PushToLua(DeleteEmitter *o)
Definition: LuaObject.h:391
static T * CheckFromLua(int idx)
Definition: LuaObject.h:232
LuaObject()
Definition: LuaObject.h:268
static bool DynamicCastPromotionTest(LuaWrappable *o)
Definition: LuaObject.h:258
static T * GetFromLua(int idx)
Definition: LuaObject.h:245
static Ret CallMethod(T *o, const Key &key, const Args &... args)
Definition: LuaObject.h:420
static void RegisterClass()
Definition: LuaObject.h:367
LuaWrappable * GetObject() const
Definition: LuaObject.h:379
~LuaOwnObject()
Definition: LuaObject.h:374
LuaOwnObject(T *o)
Definition: LuaObject.h:369
Definition: LuaRef.h:12
Definition: LuaSerializer.h:12
Definition: LuaObject.h:320
LuaSharedObject(T *o)
Definition: LuaObject.h:322
LuaWrappable * GetObject() const
Definition: LuaObject.h:325
Definition: LuaWrappable.h:13
Definition: PropertyMap.h:11
Definition: RefCounted.h:36
Definition: RefCounted.h:11
Definition: SystemPath.h:13
const Color4ub * GetFromLua(lua_State *L, int idx)
Definition: LuaColor.cpp:303
void PushToLua(lua_State *L, const Color4ub &v)
Definition: LuaColor.h:14
Color4ub * CheckFromLua(lua_State *L, int idx)
Definition: LuaColor.cpp:308
void Register(lua_State *L)
Definition: LuaColor.cpp:273
LuaManager * manager
Definition: Lua.cpp:43
Definition: GeomTree.h:9
Definition: LuaObject.h:77
ToJson to_json
Definition: LuaObject.h:102
FromJson from_json
Definition: LuaObject.h:103
bool(* FromJson)(const Json &obj)
Definition: LuaObject.h:82
SerializerPair()
Definition: LuaObject.h:84
Serializer serialize
Definition: LuaObject.h:100
Deserializer deserialize
Definition: LuaObject.h:101
bool(* Deserializer)(const char *stream, const char **next)
Definition: LuaObject.h:79
void(* ToJson)(Json &out, LuaWrappable *o)
Definition: LuaObject.h:81
SerializerPair(Serializer serialize_, Deserializer deserialize_, ToJson to_json_, FromJson from_json_)
Definition: LuaObject.h:91