Pioneer
LuaTable.h
Go to the documentation of this file.
1 // Copyright © 2008-2023 Pioneer Developers. See AUTHORS.txt for details
2 // Licensed under the terms of the GPL v3. See licenses/GPL-3.txt
3 
4 #ifndef _LUATABLE_H
5 #define _LUATABLE_H
6 
7 #include <cassert>
8 #include <iterator>
9 
10 #include <lua.hpp>
11 
12 #include "LuaPushPull.h"
13 #include "LuaRef.h"
14 #include "LuaUtils.h"
15 
16 /*
17  * The LuaTable class is a wrapper around a table present on the stack. There
18  * are three ways to instantiate a LuaTable object:
19  *
20  * > lua_State *l;
21  * > int i; // the stack index of a table
22  * > LuaTable(l); // This will allocate a new table on top of the stack
23  * > LuaTable(l, array_s, hash_s); // Same as previous but it uses "lua_createtable",
24  * > // so it preallocate array part and hash part on LVM
25  * > LuaTable(l, i); // This will wrap the object around an existing table
26  *
27  * Note that the LuaTable object never removes the wrapped table from the stack.
28  * Also, there is no integrity check except at the object creation, which means
29  * that if you fiddle with the stack below the wrapped table you will get
30  * unexpected results (most likely a crash).
31  *
32  * Get/Set:
33  *
34  * The Get and Set operators use the pi_lua_generic_{push pull} functions
35  * to fetch a value of any type from the table. It is possible to add support
36  * for extra types by overloading the aforementioned functions for the new
37  * type. The Get function takes an optional default value, which will be returned
38  * if the table does not contain the requested key. If no default is given, and
39  * the key is not in the table then a Lua error is generated. If the key is present
40  * but the value has an incompatible type, then a Lua error is generated (even if
41  * a default value is passed to the Get method).
42  *
43  * These operations are designed to restore the state of the stack, thus making
44  * it impossible to Get a LuaTable, as the latter would need a place on the
45  * stack. For this reason, the Sub() method is used to get a subtable,
46  * placing the "returned" table on the top of the stack.
47  *
48  * Example:
49  *
50  * > lua_State *l; // stack size: X
51  * > LuaTable t = LuaTable(l+1); // stack size: X+1, t = {}
52  * > t.Set("foo", 1); // stack size: X+1, t = {foo:1}
53  * > int foo = t.Get<int>("foo");
54  * > //int bar = t.Get<int>("bar"); // WOULD CRASH!
55  * > int bar = t.Get("bar", 0);
56  * > {
57  * > LuaTable t2(l); // stack size: X+2
58  * > t.Set("baz", t2); // t = {foo:1, baz:<t2>}
59  * > } // t2 isn't a valid name, we can now safely pop the table out.
60  * > lua_pop(l, 1); // stack size: X+1
61  * > LuaTable t2_bis = t.Sub("baz"); // stack size: X+2
62  *
63  * STL loaders:
64  *
65  * If you want to load a whole vector or map into a LuaTable, just do
66  *
67  * > std::vector v; // Or std::list, std::set, whatever as long as it has iterators
68  * > std::map m;
69  * > LuaTable t;
70  * > t.LoadMap(m.begin(), m.end());
71  * > t.LoadVector(v.begin(), v.end());
72  *
73  * Note that LoadVector doesn't overwrite the content of the table, it appends
74  * to its array-like part. Unless you have numerical index beyond its length,
75  * which you shouldn't do anyway.
76  *
77  * LoadMap happily overwrites any data if necessary.
78  *
79  * VecIter:
80  *
81  * It is possible to get STL-like iterators on the array part of the table.
82  * The use cases are typically in loops or to use in the STL algorithms or as
83  * inputs for containers.
84  *
85  * The two methods are LuaTable::Begin<Value>() and LuaTable::End<Value>()
86  *
87  * As usual, since C++ is static typed, the iterators will fail in a mixed-typed table
88  * (generating a Lua error when you attempt to access an element with the wrong type).
89  *
90  * ScopedTable:
91  *
92  * The ScopedTable class is a LuaTable derivative that comes with two constructors:
93  * * New table constructor: ScopedTable(l);
94  * * LuaRef contructor: ScopedTable(my_lua_ref_object);
95  * Both constructors will push a new table onto the stack, and when the C++
96  * ScopedTable objects are destroyed, this new table is removed and everything
97  * above it on the stack gets shifted down.
98  */
99 class LuaTable {
100 public:
101  // For now, every lua_State * can only be NULL or Pi::LuaManager->GetLuaState();
102  LuaTable(const LuaTable &ref) :
103  m_lua(ref.m_lua),
104  m_index(ref.m_index) {} // Copy constructor.
105  LuaTable(lua_State *l, int index) :
106  m_lua(l),
107  m_index(lua_absindex(l, index)) { assert(lua_istable(m_lua, m_index)); }
108  explicit LuaTable(lua_State *l) :
109  m_lua(l)
110  {
111  lua_newtable(m_lua);
112  m_index = lua_gettop(l);
113  }
114 
115  explicit LuaTable(lua_State *l, int array_s, int hash_s) :
116  m_lua(l)
117  {
118  lua_createtable(m_lua, array_s, hash_s);
119  m_index = lua_gettop(l);
120  }
121 
123 
124  const LuaTable &operator=(const LuaTable &ref)
125  {
126  m_lua = ref.m_lua;
127  m_index = ref.m_index;
128  return *this;
129  }
130  template <class Key>
131  LuaTable PushValueToStack(const Key &key) const;
132  template <class Value, class Key>
133  Value Get(const Key &key) const;
134  template <class Key>
135  LuaTable Sub(const Key &key) const; // Does not clean up the stack.
136  template <class Value, class Key>
137  Value Get(const Key &key, Value default_value) const;
138  template <class Value, class Key>
139  LuaTable Set(const Key &key, const Value &value) const;
140 
141  template <class Ret, class Key, class... Args>
142  Ret Call(const Key &key, const Args &... args) const;
143  template <class Key, class... Args>
144  void Call(const Key &key, const Args &... args) const
145  {
146  Call<bool>(key, args...);
147  }
148  template <class Ret1, class Ret2, class... Ret, class Key, class... Args>
149  std::tuple<Ret1, Ret2, Ret...> Call(const Key &key, const Args &... args) const;
150 
151  template <class Key, class... Args>
152  void CallMethod(const Key &key, const Args &... args) const
153  {
154  Call<bool>(key, *this, args...);
155  }
156  template <class Ret, class Key, class... Args>
157  Ret CallMethod(const Key &key, const Args &... args) const
158  {
159  return Call<Ret>(key, *this, args...);
160  }
161  template <class Ret1, class Ret2, class... Ret, class Key, class... Args>
162  std::tuple<Ret1, Ret2, Ret...> CallMethod(const Key &key, const Args &... args) const
163  {
164  return Call<Ret1, Ret2, Ret...>(key, *this, args...);
165  }
166 
167  template <class PairIterator>
168  LuaTable LoadMap(PairIterator beg, PairIterator end) const;
169  template <class ValueIterator>
170  LuaTable LoadVector(ValueIterator beg, ValueIterator end) const;
171 
172  template <class Key, class Value>
173  std::map<Key, Value> GetMap() const;
174 
175  lua_State *GetLua() const { return m_lua; }
176  int GetIndex() const { return m_index; }
177  size_t Size() const { return lua_rawlen(m_lua, m_index); }
178 
179  /* VecIter, as in VectorIterator (only shorter to type :-)
180  *
181  * Careful, its LuaTable specialization isn't stable WRT the stack, and using its value
182  * will push a table onto the stack, and will only clean it up when the iterator
183  * gets destroyed or inc/decremented.
184  *
185  * For all other values, occasional operations on the stack may occur but it should
186  * not leak anything.
187  */
188  template <class Value>
189  class VecIter : public std::iterator<std::input_iterator_tag, Value> {
190  public:
192  m_table(0),
193  m_currentIndex(0),
194  m_cache(),
195  m_dirtyCache(true) {}
196  ~VecIter() {}
197  VecIter(LuaTable *t, int currentIndex) :
198  m_table(t),
199  m_currentIndex(currentIndex),
200  m_cache(),
201  m_dirtyCache(true) {}
202  VecIter(const VecIter &copy) :
203  m_table(copy.m_table),
204  m_currentIndex(copy.m_currentIndex),
205  m_cache(),
206  m_dirtyCache(true) {}
207  void operator=(const VecIter &copy)
208  {
209  CleanCache();
210  m_table = copy.m_table;
211  m_currentIndex = copy.m_currentIndex;
212  }
213 
215  {
216  if (m_table) {
217  CleanCache();
218  ++m_currentIndex;
219  }
220  return *this;
221  }
223  {
224  VecIter copy(*this);
225  if (m_table) {
226  CleanCache();
227  ++m_currentIndex;
228  }
229  return copy;
230  }
232  {
233  if (m_table) --m_currentIndex;
234  return *this;
235  }
237  {
238  VecIter copy(*this);
239  if (m_table) --m_currentIndex;
240  return copy;
241  }
242 
243  bool operator==(const VecIter &other) const { return (m_table == other.m_table && m_currentIndex == other.m_currentIndex); }
244  bool operator!=(const VecIter &other) const { return (m_table != other.m_table || m_currentIndex != other.m_currentIndex); }
246  {
247  LoadCache();
248  return m_cache;
249  }
250  const Value *operator->()
251  {
252  LoadCache();
253  return &m_cache;
254  }
255 
256  private:
257  void CleanCache() { m_dirtyCache = true; }
258  void LoadCache()
259  {
260  if (m_dirtyCache) {
261  m_cache = m_table->Get<Value>(m_currentIndex);
262  m_dirtyCache = false;
263  }
264  }
265  LuaTable *m_table;
266  int m_currentIndex;
267  Value m_cache;
268  bool m_dirtyCache;
269  };
270 
271  template <class Value>
272  VecIter<Value> Begin() { return VecIter<Value>(this, 1); }
273  template <class Value>
274  VecIter<Value> End() { return VecIter<Value>(this, Size() + 1); }
275 
276 protected:
278  m_lua(0),
279  m_index(0) {} //Protected : invalid tables shouldn't be out there.
280  lua_State *m_lua;
281  int m_index;
282 };
283 
284 class ScopedTable : public LuaTable {
285 public:
286  ScopedTable(const LuaTable &t) :
287  LuaTable(t)
288  {
289  if (m_lua) {
290  lua_pushvalue(m_lua, m_index);
291  m_index = lua_gettop(m_lua);
292  }
293  }
294  ScopedTable(lua_State *l) :
295  LuaTable(l) {}
296  ScopedTable(const LuaRef &r) :
297  LuaTable()
298  {
299  r.PushCopyToStack();
300  m_lua = r.GetLua();
301  m_index = lua_gettop(m_lua);
302  }
304  {
305  if (m_lua && !lua_isnone(m_lua, m_index) && lua_istable(m_lua, m_index))
306  lua_remove(m_lua, m_index);
307  }
308 };
309 
310 template <class Key>
312 {
314  lua_gettable(m_lua, m_index);
315  return *this;
316 }
317 
318 template <class Key>
319 LuaTable LuaTable::Sub(const Key &key) const
320 {
321  PushValueToStack(key);
322  return (lua_istable(m_lua, -1)) ? LuaTable(m_lua, -1) : LuaTable();
323 }
324 
325 template <class Value, class Key>
326 Value LuaTable::Get(const Key &key) const
327 {
328  Value return_value;
329  PushValueToStack(key);
330  pi_lua_generic_pull(m_lua, -1, return_value);
331  lua_pop(m_lua, 1);
332  return return_value;
333 }
334 
335 template <class Value, class Key>
336 Value LuaTable::Get(const Key &key, Value default_value) const
337 {
338  PushValueToStack(key);
339  if (!(lua_isnil(m_lua, -1)))
340  pi_lua_generic_pull(m_lua, -1, default_value);
341  lua_pop(m_lua, 1);
342  return default_value;
343 }
344 
345 template <class Value, class Key>
346 LuaTable LuaTable::Set(const Key &key, const Value &value) const
347 {
349  pi_lua_generic_push(m_lua, value);
350  lua_settable(m_lua, m_index);
351  return *this;
352 }
353 
354 template <class Key, class Value>
355 std::map<Key, Value> LuaTable::GetMap() const
356 {
358  std::map<Key, Value> ret;
359  lua_pushnil(m_lua);
360  while (lua_next(m_lua, m_index)) {
361  Key k;
362  Value v;
363  if (pi_lua_strict_pull(m_lua, -2, k) && pi_lua_strict_pull(m_lua, -1, v)) {
364  ret[k] = v;
365  } else {
366  // XXX we should probably emit some kind of warning here somehow
367  }
368  lua_pop(m_lua, 1);
369  }
370  LUA_DEBUG_END(m_lua, 0);
371  return ret;
372 }
373 
374 template <class PairIterator>
375 LuaTable LuaTable::LoadMap(PairIterator beg, PairIterator end) const
376 {
377  for (PairIterator it = beg; it != end; ++it)
378  Set(it->first, it->second);
379  return *this;
380 }
381 
382 template <class ValueIterator>
383 LuaTable LuaTable::LoadVector(ValueIterator beg, ValueIterator end) const
384 {
385  lua_len(m_lua, m_index);
386  int i = lua_tointeger(m_lua, -1) + 1;
387  lua_pop(m_lua, 1);
388  for (ValueIterator it = beg; it != end; ++it, ++i)
389  Set(i, *it);
390  return *this;
391 }
392 
393 template <class Ret, class Key, class... Args>
394 Ret LuaTable::Call(const Key &key, const Args &... args) const
395 {
397  Ret return_value;
398 
399  lua_checkstack(m_lua, sizeof...(args) + 3);
400  PushValueToStack(key);
401  pi_lua_multiple_push(m_lua, args...);
402  pi_lua_protected_call(m_lua, sizeof...(args), 1);
403  pi_lua_generic_pull(m_lua, -1, return_value);
404  lua_pop(m_lua, 1);
405  LUA_DEBUG_END(m_lua, 0);
406  return return_value;
407 }
408 
409 template <class Ret1, class Ret2, class... Ret, class Key, class... Args>
410 std::tuple<Ret1, Ret2, Ret...> LuaTable::Call(const Key &key, const Args &... args) const
411 {
413  lua_checkstack(m_lua, sizeof...(args) + 3);
414  PushValueToStack(key);
415  pi_lua_multiple_push(m_lua, args...);
416  pi_lua_protected_call(m_lua, sizeof...(args), sizeof...(Ret) + 2);
417  auto return_values = pi_lua_multiple_pull<Ret1, Ret2, Ret...>(m_lua, -static_cast<int>(sizeof...(Ret)) - 2);
418  lua_pop(m_lua, static_cast<int>(sizeof...(Ret)) + 2);
419  LUA_DEBUG_END(m_lua, 0);
420  return return_values;
421 }
422 
423 template <>
425 {
426  if (m_dirtyCache) {
427  m_cache = m_table->Sub(m_currentIndex);
428  m_dirtyCache = false;
429  }
430 }
431 template <>
433 {
434  if (!m_dirtyCache && m_cache.GetLua()) {
435  lua_remove(m_cache.GetLua(), m_cache.GetIndex());
436  }
437  m_dirtyCache = true;
438 }
439 
440 inline void pi_lua_generic_push(lua_State *l, const LuaTable &value)
441 {
442  lua_pushvalue(l, value.GetIndex());
443 }
444 #endif
void pi_lua_generic_pull(lua_State *l, int index, Color4ub *&out)
Definition: LuaColor.cpp:12
bool pi_lua_strict_pull(lua_State *l, int index, Color4ub &out)
Definition: LuaColor.h:27
std::tuple< Types... > pi_lua_multiple_pull(lua_State *l, int beg)
Definition: LuaPushPull.h:237
void pi_lua_multiple_push(lua_State *l, Types... args)
void pi_lua_generic_push(lua_State *l, const LuaTable &value)
Definition: LuaTable.h:440
#define LUA_DEBUG_START(luaptr)
Definition: LuaUtils.h:103
#define LUA_DEBUG_END(luaptr, expectedStackDiff)
Definition: LuaUtils.h:104
void pi_lua_protected_call(lua_State *L, int nargs, int nresults)
Definition: Sandbox.cpp:248
Definition: LuaRef.h:12
void PushCopyToStack() const
Definition: LuaRef.cpp:89
lua_State * GetLua() const
Definition: LuaRef.h:26
Definition: LuaTable.h:189
VecIter operator--()
Definition: LuaTable.h:231
VecIter(const VecIter &copy)
Definition: LuaTable.h:202
VecIter operator++(int)
Definition: LuaTable.h:222
Value operator*()
Definition: LuaTable.h:245
VecIter operator--(int)
Definition: LuaTable.h:236
const Value * operator->()
Definition: LuaTable.h:250
bool operator!=(const VecIter &other) const
Definition: LuaTable.h:244
void operator=(const VecIter &copy)
Definition: LuaTable.h:207
~VecIter()
Definition: LuaTable.h:196
VecIter()
Definition: LuaTable.h:191
bool operator==(const VecIter &other) const
Definition: LuaTable.h:243
VecIter(LuaTable *t, int currentIndex)
Definition: LuaTable.h:197
VecIter operator++()
Definition: LuaTable.h:214
Definition: LuaTable.h:99
Value Get(const Key &key) const
Definition: LuaTable.h:326
const LuaTable & operator=(const LuaTable &ref)
Definition: LuaTable.h:124
VecIter< Value > End()
Definition: LuaTable.h:274
lua_State * m_lua
Definition: LuaTable.h:280
lua_State * GetLua() const
Definition: LuaTable.h:175
LuaTable PushValueToStack(const Key &key) const
Definition: LuaTable.h:311
int GetIndex() const
Definition: LuaTable.h:176
LuaTable(const LuaTable &ref)
Definition: LuaTable.h:102
void Call(const Key &key, const Args &... args) const
Definition: LuaTable.h:144
LuaTable Set(const Key &key, const Value &value) const
Definition: LuaTable.h:346
~LuaTable()
Definition: LuaTable.h:122
std::tuple< Ret1, Ret2, Ret... > CallMethod(const Key &key, const Args &... args) const
Definition: LuaTable.h:162
std::map< Key, Value > GetMap() const
Definition: LuaTable.h:355
LuaTable LoadVector(ValueIterator beg, ValueIterator end) const
Definition: LuaTable.h:383
LuaTable(lua_State *l)
Definition: LuaTable.h:108
Ret Call(const Key &key, const Args &... args) const
Definition: LuaTable.h:394
Ret CallMethod(const Key &key, const Args &... args) const
Definition: LuaTable.h:157
LuaTable(lua_State *l, int index)
Definition: LuaTable.h:105
int m_index
Definition: LuaTable.h:281
LuaTable(lua_State *l, int array_s, int hash_s)
Definition: LuaTable.h:115
size_t Size() const
Definition: LuaTable.h:177
LuaTable()
Definition: LuaTable.h:277
LuaTable LoadMap(PairIterator beg, PairIterator end) const
Definition: LuaTable.h:375
VecIter< Value > Begin()
Definition: LuaTable.h:272
LuaTable Sub(const Key &key) const
Definition: LuaTable.h:319
void CallMethod(const Key &key, const Args &... args) const
Definition: LuaTable.h:152
Definition: LuaTable.h:284
ScopedTable(lua_State *l)
Definition: LuaTable.h:294
ScopedTable(const LuaTable &t)
Definition: LuaTable.h:286
ScopedTable(const LuaRef &r)
Definition: LuaTable.h:296
~ScopedTable()
Definition: LuaTable.h:303
IMGUI_API void Value(const char *prefix, const std::string &str)
Definition: PerfInfo.cpp:181