Pioneer
Loading...
Searching...
No Matches
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 */
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
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) {}
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); }
245 Value operator*()
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
276protected:
278 m_lua(0),
279 m_index(0) {} //Protected : invalid tables shouldn't be out there.
280 lua_State *m_lua;
282};
283
284class ScopedTable : public LuaTable {
285public:
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
310template <class Key>
312{
314 lua_gettable(m_lua, m_index);
315 return *this;
316}
317
318template <class Key>
319LuaTable LuaTable::Sub(const Key &key) const
320{
321 PushValueToStack(key);
322 return (lua_istable(m_lua, -1)) ? LuaTable(m_lua, -1) : LuaTable();
323}
324
325template <class Value, class Key>
326Value 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
335template <class Value, class Key>
336Value 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
345template <class Value, class Key>
346LuaTable LuaTable::Set(const Key &key, const Value &value) const
347{
350 lua_settable(m_lua, m_index);
351 return *this;
352}
353
354template <class Key, class Value>
355std::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 }
371 return ret;
372}
373
374template <class PairIterator>
375LuaTable 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
382template <class ValueIterator>
383LuaTable 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
393template <class Ret, class Key, class... Args>
394Ret 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);
406 return return_value;
407}
408
409template <class Ret1, class Ret2, class... Ret, class Key, class... Args>
410std::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);
420 return return_values;
421}
422
423template <>
425{
426 if (m_dirtyCache) {
427 m_cache = m_table->Sub(m_currentIndex);
428 m_dirtyCache = false;
429 }
430}
431template <>
433{
434 if (!m_dirtyCache && m_cache.GetLua()) {
435 lua_remove(m_cache.GetLua(), m_cache.GetIndex());
436 }
437 m_dirtyCache = true;
438}
439
440inline 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
lua_State * GetLua() const
Definition LuaRef.h:26
void PushCopyToStack() const
Definition LuaRef.cpp:89
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
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
const Value * operator->()
Definition LuaTable.h:250
Definition LuaTable.h:99
Value Get(const Key &key) const
Definition LuaTable.h:326
lua_State * m_lua
Definition LuaTable.h:280
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
std::tuple< Ret1, Ret2, Ret... > CallMethod(const Key &key, const Args &... args) const
Definition LuaTable.h:162
LuaTable Set(const Key &key, const Value &value) const
Definition LuaTable.h:346
VecIter< Value > End()
Definition LuaTable.h:274
~LuaTable()
Definition LuaTable.h:122
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
VecIter< Value > Begin()
Definition LuaTable.h:272
LuaTable(lua_State *l, int array_s, int hash_s)
Definition LuaTable.h:115
lua_State * GetLua() const
Definition LuaTable.h:175
size_t Size() const
Definition LuaTable.h:177
LuaTable()
Definition LuaTable.h:277
LuaTable LoadMap(PairIterator beg, PairIterator end) const
Definition LuaTable.h:375
const LuaTable & operator=(const LuaTable &ref)
Definition LuaTable.h:124
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