Pioneer
Loading...
Searching...
No Matches
LuaTable.h
Go to the documentation of this file.
1// Copyright © 2008-2024 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 */
99class LuaTable {
100public:
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 // Push the passed value to the end of the array portion of this table
141 template <class Value>
142 LuaTable &PushBack(Value &value);
143 // Push all passed values in-order to the end of the array portion of this table
144 template <typename... Values>
145 LuaTable &PushMultiple(Values &... value);
146
147 template <class Ret, class Key, class... Args>
148 Ret Call(const Key &key, const Args &... args) const;
149 template <class Key, class... Args>
150 void Call(const Key &key, const Args &... args) const
151 {
152 Call<bool>(key, args...);
153 }
154 template <class Ret1, class Ret2, class... Ret, class Key, class... Args>
155 std::tuple<Ret1, Ret2, Ret...> Call(const Key &key, const Args &... args) const;
156
157 template <class Key, class... Args>
158 void CallMethod(const Key &key, const Args &... args) const
159 {
160 Call<bool>(key, *this, args...);
161 }
162 template <class Ret, class Key, class... Args>
163 Ret CallMethod(const Key &key, const Args &... args) const
164 {
165 return Call<Ret>(key, *this, args...);
166 }
167 template <class Ret1, class Ret2, class... Ret, class Key, class... Args>
168 std::tuple<Ret1, Ret2, Ret...> CallMethod(const Key &key, const Args &... args) const
169 {
170 return Call<Ret1, Ret2, Ret...>(key, *this, args...);
171 }
172
173 template <class PairIterator>
174 LuaTable LoadMap(PairIterator beg, PairIterator end) const;
175 template <class ValueIterator>
176 LuaTable LoadVector(ValueIterator beg, ValueIterator end) const;
177
178 template <class Key, class Value>
179 std::map<Key, Value> GetMap() const;
180
181 lua_State *GetLua() const { return m_lua; }
182 int GetIndex() const { return m_index; }
183 size_t Size() const { return lua_rawlen(m_lua, m_index); }
184
185 /* VecIter, as in VectorIterator (only shorter to type :-)
186 *
187 * Careful, its LuaTable specialization isn't stable WRT the stack, and using its value
188 * will push a table onto the stack, and will only clean it up when the iterator
189 * gets destroyed or inc/decremented.
190 *
191 * For all other values, occasional operations on the stack may occur but it should
192 * not leak anything.
193 */
194 template <class Value>
195 class VecIter {
196 public:
197 // iterator category defines
198 using difference_type = std::ptrdiff_t;
199 using value_type = Value;
200 using pointer = Value *;
201 using reference = Value &;
202 using iterator_category = std::input_iterator_tag;
203
204 public:
206 m_table(0),
207 m_currentIndex(0),
208 m_cache(),
209 m_dirtyCache(true) {}
211 VecIter(LuaTable *t, int currentIndex) :
212 m_table(t),
213 m_currentIndex(currentIndex),
214 m_cache(),
215 m_dirtyCache(true) {}
216 VecIter(const VecIter &copy) :
217 m_table(copy.m_table),
218 m_currentIndex(copy.m_currentIndex),
219 m_cache(),
220 m_dirtyCache(true) {}
221 void operator=(const VecIter &copy)
222 {
223 CleanCache();
224 m_table = copy.m_table;
225 m_currentIndex = copy.m_currentIndex;
226 }
227
229 {
230 if (m_table) {
231 CleanCache();
232 ++m_currentIndex;
233 }
234 return *this;
235 }
237 {
238 VecIter copy(*this);
239 if (m_table) {
240 CleanCache();
241 ++m_currentIndex;
242 }
243 return copy;
244 }
246 {
247 if (m_table) --m_currentIndex;
248 return *this;
249 }
251 {
252 VecIter copy(*this);
253 if (m_table) --m_currentIndex;
254 return copy;
255 }
256
257 bool operator==(const VecIter &other) const { return (m_table == other.m_table && m_currentIndex == other.m_currentIndex); }
258 bool operator!=(const VecIter &other) const { return (m_table != other.m_table || m_currentIndex != other.m_currentIndex); }
259 Value operator*()
260 {
261 LoadCache();
262 return m_cache;
263 }
264 const Value *operator->()
265 {
266 LoadCache();
267 return &m_cache;
268 }
269
270 private:
271 void CleanCache() { m_dirtyCache = true; }
272 void LoadCache()
273 {
274 if (m_dirtyCache) {
275 m_cache = m_table->Get<Value>(m_currentIndex);
276 m_dirtyCache = false;
277 }
278 }
279 LuaTable *m_table;
280 int m_currentIndex;
281 Value m_cache;
282 bool m_dirtyCache;
283 };
284
285 template <class Value>
286 VecIter<Value> Begin() { return VecIter<Value>(this, 1); }
287 template <class Value>
288 VecIter<Value> End() { return VecIter<Value>(this, Size() + 1); }
289
290protected:
292 m_lua(0),
293 m_index(0) {} //Protected : invalid tables shouldn't be out there.
294 lua_State *m_lua;
296};
297
298class ScopedTable : public LuaTable {
299public:
301 LuaTable(t)
302 {
303 if (m_lua) {
304 lua_pushvalue(m_lua, m_index);
305 m_index = lua_gettop(m_lua);
306 }
307 }
308 ScopedTable(lua_State *l) :
309 LuaTable(l) {}
310 ScopedTable(const LuaRef &r) :
311 LuaTable()
312 {
313 r.PushCopyToStack();
314 m_lua = r.GetLua();
315 m_index = lua_gettop(m_lua);
316 }
317
318 // Cannot copy a scoped table
319 ScopedTable(const ScopedTable &) = delete;
321
323 {
324 if (m_lua && !lua_isnone(m_lua, m_index) && lua_istable(m_lua, m_index))
325 lua_remove(m_lua, m_index);
326 }
327};
328
329template <class Key>
331{
333 lua_gettable(m_lua, m_index);
334 return *this;
335}
336
337template <class Key>
338LuaTable LuaTable::Sub(const Key &key) const
339{
340 PushValueToStack(key);
341 return (lua_istable(m_lua, -1)) ? LuaTable(m_lua, -1) : LuaTable();
342}
343
344template <class Value, class Key>
345Value LuaTable::Get(const Key &key) const
346{
347 Value return_value;
348 PushValueToStack(key);
349 pi_lua_generic_pull(m_lua, -1, return_value);
350 lua_pop(m_lua, 1);
351 return return_value;
352}
353
354template <class Value, class Key>
355Value LuaTable::Get(const Key &key, Value default_value) const
356{
357 PushValueToStack(key);
358 if (!(lua_isnil(m_lua, -1)))
359 pi_lua_generic_pull(m_lua, -1, default_value);
360 lua_pop(m_lua, 1);
361 return default_value;
362}
363
364template <class Value, class Key>
365LuaTable LuaTable::Set(const Key &key, const Value &value) const
366{
369 lua_settable(m_lua, m_index);
370 return *this;
371}
372
373template <class Value>
375{
378 lua_settable(m_lua, m_index);
379 return *this;
380}
381
382template <typename... Values>
384{
385 (..., PushBack<Values>(values));
386 return *this;
387}
388
389template <class Key, class Value>
390std::map<Key, Value> LuaTable::GetMap() const
391{
393 std::map<Key, Value> ret;
394 lua_pushnil(m_lua);
395 while (lua_next(m_lua, m_index)) {
396 Key k;
397 Value v;
398 if (pi_lua_strict_pull(m_lua, -2, k) && pi_lua_strict_pull(m_lua, -1, v)) {
399 ret[k] = v;
400 } else {
401 // XXX we should probably emit some kind of warning here somehow
402 }
403 lua_pop(m_lua, 1);
404 }
406 return ret;
407}
408
409template <class PairIterator>
410LuaTable LuaTable::LoadMap(PairIterator beg, PairIterator end) const
411{
412 for (PairIterator it = beg; it != end; ++it)
413 Set(it->first, it->second);
414 return *this;
415}
416
417template <class ValueIterator>
418LuaTable LuaTable::LoadVector(ValueIterator beg, ValueIterator end) const
419{
420 lua_len(m_lua, m_index);
421 int i = lua_tointeger(m_lua, -1) + 1;
422 lua_pop(m_lua, 1);
423 for (ValueIterator it = beg; it != end; ++it, ++i)
424 Set(i, *it);
425 return *this;
426}
427
428template <class Ret, class Key, class... Args>
429Ret LuaTable::Call(const Key &key, const Args &... args) const
430{
432 Ret return_value;
433
434 lua_checkstack(m_lua, sizeof...(args) + 3);
435 PushValueToStack(key);
436 pi_lua_multiple_push(m_lua, args...);
437 pi_lua_protected_call(m_lua, sizeof...(args), 1);
438 pi_lua_generic_pull(m_lua, -1, return_value);
439 lua_pop(m_lua, 1);
441 return return_value;
442}
443
444template <class Ret1, class Ret2, class... Ret, class Key, class... Args>
445std::tuple<Ret1, Ret2, Ret...> LuaTable::Call(const Key &key, const Args &... args) const
446{
448 lua_checkstack(m_lua, sizeof...(args) + 3);
449 PushValueToStack(key);
450 pi_lua_multiple_push(m_lua, args...);
451 pi_lua_protected_call(m_lua, sizeof...(args), sizeof...(Ret) + 2);
452 auto return_values = pi_lua_multiple_pull<Ret1, Ret2, Ret...>(m_lua, -static_cast<int>(sizeof...(Ret)) - 2);
453 lua_pop(m_lua, static_cast<int>(sizeof...(Ret)) + 2);
455 return return_values;
456}
457
458template <>
460{
461 if (m_dirtyCache) {
462 m_cache = m_table->Sub(m_currentIndex);
463 m_dirtyCache = false;
464 }
465}
466template <>
468{
469 if (!m_dirtyCache && m_cache.GetLua()) {
470 lua_remove(m_cache.GetLua(), m_cache.GetIndex());
471 }
472 m_dirtyCache = true;
473}
474
475inline void pi_lua_generic_push(lua_State *l, const LuaTable &value)
476{
477 lua_pushvalue(l, value.GetIndex());
478}
479#endif
void pi_lua_generic_pull(lua_State *l, int index, ObjectType &objectType)
Definition: LuaBody.cpp:865
bool pi_lua_strict_pull(lua_State *l, int index, Color4ub &out)
Definition: LuaColor.h:27
void pi_lua_multiple_push(lua_State *l, Types... args)
Definition: LuaPushPull.h:137
std::tuple< std::remove_reference_t< Types >... > pi_lua_multiple_pull(lua_State *l, int beg)
Definition: LuaPushPull.h:148
void pi_lua_generic_push(lua_State *l, const LuaTable &value)
Definition: LuaTable.h:475
#define LUA_DEBUG_START(luaptr)
Definition: LuaUtils.h:105
#define LUA_DEBUG_END(luaptr, expectedStackDiff)
Definition: LuaUtils.h:106
void pi_lua_protected_call(lua_State *L, int nargs, int nresults)
Definition: Sandbox.cpp:283
Definition: LuaRef.h:12
lua_State * GetLua() const
Definition: LuaRef.h:26
void PushCopyToStack() const
Definition: LuaRef.cpp:89
Definition: LuaTable.h:195
VecIter operator--()
Definition: LuaTable.h:245
Value value_type
Definition: LuaTable.h:199
VecIter(const VecIter &copy)
Definition: LuaTable.h:216
VecIter operator++(int)
Definition: LuaTable.h:236
Value operator*()
Definition: LuaTable.h:259
VecIter operator--(int)
Definition: LuaTable.h:250
bool operator!=(const VecIter &other) const
Definition: LuaTable.h:258
std::ptrdiff_t difference_type
Definition: LuaTable.h:198
void operator=(const VecIter &copy)
Definition: LuaTable.h:221
std::input_iterator_tag iterator_category
Definition: LuaTable.h:202
~VecIter()
Definition: LuaTable.h:210
VecIter()
Definition: LuaTable.h:205
bool operator==(const VecIter &other) const
Definition: LuaTable.h:257
VecIter(LuaTable *t, int currentIndex)
Definition: LuaTable.h:211
Value & reference
Definition: LuaTable.h:201
Value * pointer
Definition: LuaTable.h:200
VecIter operator++()
Definition: LuaTable.h:228
const Value * operator->()
Definition: LuaTable.h:264
Definition: LuaTable.h:99
Value Get(const Key &key) const
Definition: LuaTable.h:345
lua_State * m_lua
Definition: LuaTable.h:294
LuaTable PushValueToStack(const Key &key) const
Definition: LuaTable.h:330
int GetIndex() const
Definition: LuaTable.h:182
LuaTable(const LuaTable &ref)
Definition: LuaTable.h:102
void Call(const Key &key, const Args &... args) const
Definition: LuaTable.h:150
std::tuple< Ret1, Ret2, Ret... > CallMethod(const Key &key, const Args &... args) const
Definition: LuaTable.h:168
LuaTable Set(const Key &key, const Value &value) const
Definition: LuaTable.h:365
VecIter< Value > End()
Definition: LuaTable.h:288
~LuaTable()
Definition: LuaTable.h:122
std::map< Key, Value > GetMap() const
Definition: LuaTable.h:390
LuaTable LoadVector(ValueIterator beg, ValueIterator end) const
Definition: LuaTable.h:418
LuaTable & PushMultiple(Values &... value)
Definition: LuaTable.h:383
LuaTable(lua_State *l)
Definition: LuaTable.h:108
Ret Call(const Key &key, const Args &... args) const
Definition: LuaTable.h:429
Ret CallMethod(const Key &key, const Args &... args) const
Definition: LuaTable.h:163
LuaTable(lua_State *l, int index)
Definition: LuaTable.h:105
int m_index
Definition: LuaTable.h:295
VecIter< Value > Begin()
Definition: LuaTable.h:286
LuaTable(lua_State *l, int array_s, int hash_s)
Definition: LuaTable.h:115
lua_State * GetLua() const
Definition: LuaTable.h:181
size_t Size() const
Definition: LuaTable.h:183
LuaTable & PushBack(Value &value)
Definition: LuaTable.h:374
LuaTable()
Definition: LuaTable.h:291
LuaTable LoadMap(PairIterator beg, PairIterator end) const
Definition: LuaTable.h:410
const LuaTable & operator=(const LuaTable &ref)
Definition: LuaTable.h:124
LuaTable Sub(const Key &key) const
Definition: LuaTable.h:338
void CallMethod(const Key &key, const Args &... args) const
Definition: LuaTable.h:158
Definition: LuaTable.h:298
ScopedTable & operator=(const ScopedTable &)=delete
ScopedTable(const ScopedTable &)=delete
ScopedTable(lua_State *l)
Definition: LuaTable.h:308
ScopedTable(const LuaTable &t)
Definition: LuaTable.h:300
ScopedTable(const LuaRef &r)
Definition: LuaTable.h:310
~ScopedTable()
Definition: LuaTable.h:322
IMGUI_API void Value(const char *prefix, const std::string &str)
Definition: PerfInfo.cpp:188