1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
|
#pragma once
#include "debug.hpp"
#include "stackindex.hpp"
// try to detect if we are building against LuaJIT or MoonJIT
#if defined(LUA_JITLIBNAME)
#include "luajit.h"
#if (defined(__x86_64__) || defined(_M_X64) || defined(__LP64__))
#define LUAJIT_FLAVOR() 64
#else // 64 bits
#define LUAJIT_FLAVOR() 32
#endif // 64 bits
#else // LUA_JITLIBNAME
#define LUAJIT_FLAVOR() 0
#define LUA_JITLIBNAME "jit"
#endif // LUA_JITLIBNAME
#ifndef LUA_OK
#define LUA_OK 0 // doesn't exist in Lua 5.1
#endif // LUA_OK
#ifndef LUA_ERRGCMM
#define LUA_ERRGCMM 666 // doesn't exist in Lua 5.1 and Lua 5.4, we don't care about the actual value
#endif // LUA_ERRGCMM
#ifndef LUA_LOADED_TABLE
#define LUA_LOADED_TABLE "_LOADED" // doesn't exist before Lua 5.3
#endif // LUA_LOADED_TABLE
// code is now preferring Lua 5.4 API
// #################################################################################################
// a strong-typed wrapper over lua types to see them easier in a debugger
enum class [[nodiscard]] LuaType
{
NONE = LUA_TNONE,
NIL = LUA_TNIL,
BOOLEAN = LUA_TBOOLEAN,
LIGHTUSERDATA = LUA_TLIGHTUSERDATA,
NUMBER = LUA_TNUMBER,
STRING = LUA_TSTRING,
TABLE = LUA_TTABLE,
FUNCTION = LUA_TFUNCTION,
USERDATA = LUA_TUSERDATA,
THREAD = LUA_TTHREAD,
CDATA = 10 // LuaJIT CDATA
};
enum class [[nodiscard]] LuaHookMask
{
None = 0,
Call = LUA_MASKCALL,
Ret = LUA_MASKRET,
Line = LUA_MASKLINE,
Count = LUA_MASKCOUNT,
All = LUA_MASKCALL | LUA_MASKRET | LUA_MASKLINE | LUA_MASKCOUNT
};
// #################################################################################################
// add some Lua 5.3-style API when building for Lua 5.1
#if LUA_VERSION_NUM == 501
inline size_t lua_rawlen(lua_State* L_, StackIndex idx_)
{
return lua_objlen(L_, idx_);
}
void luaL_requiref(lua_State* L_, char const* modname_, lua_CFunction openf_, int glb_); // implementation copied from Lua 5.2 sources
int luaL_getsubtable(lua_State* L_, StackIndex idx_, char const* fname_);
#endif // LUA_VERSION_NUM == 501
// #################################################################################################
// wrap Lua 5.3 calls under Lua 5.1 API when it is simpler that way
#if LUA_VERSION_NUM == 503
inline int luaL_optint(lua_State* L_, int n_, lua_Integer d_)
{
return static_cast<int>(luaL_optinteger(L_, n_, d_));
}
#endif // LUA_VERSION_NUM == 503
// #################################################################################################
#if LUA_VERSION_NUM < 504
void* lua_newuserdatauv(lua_State* L_, size_t sz_, UserValueCount nuvalue_);
int lua_getiuservalue(lua_State* L_, StackIndex idx_, UserValueIndex n_);
int lua_setiuservalue(lua_State* L_, StackIndex idx_, UserValueIndex n_);
#define LUA_GNAME "_G"
#endif // LUA_VERSION_NUM < 504
// #################################################################################################
// wrap Lua 5.4 calls under Lua 5.1 API when it is simpler that way
#if LUA_VERSION_NUM == 504
inline int luaL_optint(lua_State* L_, StackIndex n_, lua_Integer d_)
{
return static_cast<int>(luaL_optinteger(L_, n_, d_));
}
#endif // LUA_VERSION_NUM == 504
// #################################################################################################
// a strong-typed wrapper over lua error codes to see them easier in a debugger
enum class [[nodiscard]] LuaError
{
OK = LUA_OK,
YIELD = LUA_YIELD,
ERRRUN = LUA_ERRRUN,
ERRSYNTAX = LUA_ERRSYNTAX,
ERRMEM = LUA_ERRMEM,
ERRGCMM = LUA_ERRGCMM,
ERRERR = LUA_ERRERR,
ERRFILE = LUA_ERRFILE
};
[[nodiscard]]
inline constexpr LuaError ToLuaError(int const rc_)
{
assert(rc_ == LUA_OK || rc_ == LUA_YIELD || rc_ == LUA_ERRRUN || rc_ == LUA_ERRSYNTAX || rc_ == LUA_ERRMEM || rc_ == LUA_ERRGCMM || rc_ == LUA_ERRERR || rc_ == LUA_ERRFILE);
return static_cast<LuaError>(rc_);
}
// #################################################################################################
// break lexical order for that one because it's needed below
[[nodiscard]]
inline LuaType luaG_type(lua_State* const L_, StackIndex const idx_)
{
return static_cast<LuaType>(lua_type(L_, idx_));
}
// #################################################################################################
// #################################################################################################
// All the compatibility wrappers we expose start with luaG_
// #################################################################################################
// #################################################################################################
// must keep as a macro as long as we do constant string concatenations
#define STRINGVIEW_FMT "%.*s"
// a replacement of lua_tolstring
[[nodiscard]]
inline std::string_view luaG_tostring(lua_State* const L_, StackIndex const idx_)
{
size_t _len{ 0 };
char const* _str{ lua_tolstring(L_, idx_, &_len) };
return _str ? std::string_view{ _str, _len } : "";
}
[[nodiscard]]
inline std::string_view luaG_checkstring(lua_State* const L_, StackIndex const idx_)
{
size_t _len{ 0 };
char const* _str{ luaL_checklstring(L_, idx_, &_len) };
return std::string_view{ _str, _len };
}
[[nodiscard]]
inline std::string_view luaG_optstring(lua_State* const L_, StackIndex const idx_, std::string_view const& default_)
{
if (lua_isnoneornil(L_, idx_)) {
return default_;
}
size_t _len{ 0 };
char const* _str{ luaL_optlstring(L_, idx_, default_.data(), &_len) };
return std::string_view{ _str, _len };
}
template <typename... EXTRA>
inline std::string_view luaG_pushstring(lua_State* const L_, std::string_view const& str_, EXTRA&&... extra_)
{
if constexpr (sizeof...(EXTRA) == 0) {
if constexpr (LUA_VERSION_NUM == 501) {
// lua_pushlstring doesn't return a value in Lua 5.1
lua_pushlstring(L_, str_.data(), str_.size());
return luaG_tostring(L_, kIdxTop);
} else {
return std::string_view{ lua_pushlstring(L_, str_.data(), str_.size()), str_.size() };
}
} else {
static_assert((... && std::is_trivial_v<std::decay_t<EXTRA>>));
return std::string_view{ lua_pushfstring(L_, str_.data(), std::forward<EXTRA>(extra_)...) };
}
}
// #################################################################################################
// use this in place of lua_absindex to save a function call
inline StackIndex luaG_absindex(lua_State* const L_, StackIndex const idx_)
{
return StackIndex{ (idx_ >= 0 || idx_ <= kIdxRegistry) ? idx_ : StackIndex{ lua_gettop(L_) + idx_ + 1 } };
}
// #################################################################################################
template <typename LUA_DUMP>
concept RequiresOldLuaDump = requires(LUA_DUMP f_) { { f_(nullptr, nullptr, nullptr) } -> std::same_as<int>; };
template <RequiresOldLuaDump LUA_DUMP>
static inline int WrapLuaDump(LUA_DUMP f_, lua_State* const L_, lua_Writer const writer_, void* const data_, [[maybe_unused]] int const strip_)
{
return f_(L_, writer_, data_);
}
// -------------------------------------------------------------------------------------------------
template <typename LUA_DUMP>
concept RequiresNewLuaDump = requires(LUA_DUMP f_) { { f_(nullptr, nullptr, nullptr, 0) } -> std::same_as<int>; };
template <RequiresNewLuaDump LUA_DUMP>
static inline int WrapLuaDump(LUA_DUMP f_, lua_State* const L_, lua_Writer const writer_, void* const data_, int const strip_)
{
return f_(L_, writer_, data_, strip_);
}
// -------------------------------------------------------------------------------------------------
static inline int luaG_dump(lua_State* const L_, lua_Writer const writer_, void* const data_, int const strip_)
{
return WrapLuaDump(lua_dump, L_, writer_, data_, strip_);
}
// #################################################################################################
UserValueCount luaG_getalluservalues(lua_State* L_, StackIndex idx_);
// #################################################################################################
template <typename LUA_GETFIELD>
concept RequiresOldLuaGetfield = requires(LUA_GETFIELD f_)
{
{
f_(nullptr, 0, nullptr)
} -> std::same_as<void>;
};
template <RequiresOldLuaGetfield LUA_GETFIELD>
static inline int WrapLuaGetField(LUA_GETFIELD f_, lua_State* const L_, StackIndex const idx_, std::string_view const& name_)
{
f_(L_, idx_, name_.data());
return lua_type(L_, -1);
}
// -------------------------------------------------------------------------------------------------
template <typename LUA_GETFIELD>
concept RequiresNewLuaGetfield = requires(LUA_GETFIELD f_)
{
{
f_(nullptr, 0, nullptr)
} -> std::same_as<int>;
};
template <RequiresNewLuaGetfield LUA_GETFIELD>
static inline int WrapLuaGetField(LUA_GETFIELD f_, lua_State* const L_, StackIndex const idx_, std::string_view const& name_)
{
return f_(L_, idx_, name_.data());
}
// -------------------------------------------------------------------------------------------------
[[nodiscard]]
static inline LuaType luaG_getfield(lua_State* const L_, StackIndex const idx_, std::string_view const& name_)
{
return static_cast<LuaType>(WrapLuaGetField(lua_getfield, L_, idx_, name_));
}
// #################################################################################################
[[nodiscard]]
LuaType luaG_getmodule(lua_State* L_, std::string_view const& name_);
// #################################################################################################
template<typename ENUM>
requires std::is_enum_v<ENUM>
[[nodiscard]]
ENUM luaG_optenum(lua_State* const L_, StackIndex const idx_, ENUM const def_)
{
return static_cast<ENUM>(luaL_optinteger(L_, idx_, static_cast<std::underlying_type_t<ENUM>>(def_)));
}
// #################################################################################################
inline void luaG_registerlibfuncs(lua_State* const L_, luaL_Reg const* funcs_)
{
// fake externs to make clang happy...
extern void luaL_register(lua_State*, char const*, luaL_Reg const*); // Lua 5.1
extern void luaL_setfuncs(lua_State* const L_, luaL_Reg const funcs_[], int nup_); // Lua 5.2+
if constexpr (LUA_VERSION_NUM == 501) {
luaL_register(L_, nullptr, funcs_);
} else {
luaL_setfuncs(L_, funcs_, 0);
}
}
// #################################################################################################
template <typename LUA_RESUME>
concept RequiresLuaResume51 = requires(LUA_RESUME f_) { { f_(nullptr, 0) } -> std::same_as<int>; };
template <RequiresLuaResume51 LUA_RESUME>
static inline int WrapLuaResume(LUA_RESUME const lua_resume_, lua_State* const L_, [[maybe_unused]] lua_State* const from_, int const nargs_, int* const nresults_)
{
// lua_resume is supposed to be called from a "clean" stack:
// it should only contain the function and its initial arguments on first call, or the resume arguments on subsequent invocations
int const _rc{ lua_resume_(L_, nargs_) };
// after resuming, the stack should only contain the yielded values
*nresults_ = lua_gettop(L_);
return _rc;
}
// -------------------------------------------------------------------------------------------------
template <typename LUA_RESUME>
concept RequiresLuaResume52 = requires(LUA_RESUME f_) { { f_(nullptr, nullptr, 0) } -> std::same_as<int>; };
template <RequiresLuaResume52 LUA_RESUME>
static inline int WrapLuaResume(LUA_RESUME const lua_resume_, lua_State* const L_, lua_State* const from_, int const nargs_, [[maybe_unused]] int* const nresults_)
{
// lua_resume is supposed to be called from a "clean" stack:
// it should only contain the function and its initial arguments on first call, or the resume arguments on subsequent invocations
int const _rc{ lua_resume_(L_, from_, nargs_) };
// after resuming, the stack should only contain the yielded values
*nresults_ = lua_gettop(L_);
return _rc;
}
// -------------------------------------------------------------------------------------------------
template <typename LUA_RESUME>
concept RequiresLuaResume54 = requires(LUA_RESUME f_) { { f_(nullptr, nullptr, 0, nullptr) } -> std::same_as<int>; };
template <RequiresLuaResume54 LUA_RESUME>
static inline int WrapLuaResume(LUA_RESUME const lua_resume_, lua_State* const L_, lua_State* const from_, int const nargs_, int* const nresults_)
{
// starting with Lua 5.4, the stack can contain stuff below the actual yielded values, but lua_resume tells us the correct nresult
return lua_resume_(L_, from_, nargs_, nresults_);
}
// -------------------------------------------------------------------------------------------------
[[nodiscard]]
static inline LuaError luaG_resume(lua_State* const L_, lua_State* const from_, int const nargs_, int* const nresults_)
{
return ToLuaError(WrapLuaResume(lua_resume, L_, from_, nargs_, nresults_));
}
// #################################################################################################
template <typename LUA_RAWGET>
concept RequiresOldLuaRawget = requires(LUA_RAWGET f_) { { f_(nullptr, 0) } -> std::same_as<void>; };
template <RequiresOldLuaRawget LUA_RAWGET>
static inline LuaType WrapLuaRawget(LUA_RAWGET lua_rawget_, lua_State* const L_, StackIndex const idx_)
{
// until Lua 5.3, lua_rawget -> void
lua_rawget_(L_, idx_);
return luaG_type(L_, kIdxTop);
}
// -------------------------------------------------------------------------------------------------
template <typename LUA_RAWGET>
concept RequiresNewLuaRawget = requires(LUA_RAWGET f_) { { f_(nullptr, 0) } -> std::same_as<int>; };
template <RequiresNewLuaRawget LUA_RAWGET>
static inline LuaType WrapLuaRawget(LUA_RAWGET lua_rawget_, lua_State* const L_, StackIndex const idx_)
{
// starting with Lua 5.3, lua_rawget -> int (the type of the extracted value)
return static_cast<LuaType>(lua_rawget_(L_, idx_));
}
// -------------------------------------------------------------------------------------------------
static inline LuaType luaG_rawget(lua_State* const L_, StackIndex const idx_)
{
return WrapLuaRawget(lua_rawget, L_, idx_);
}
// #################################################################################################
static inline LuaType luaG_rawgetfield(lua_State* const L_, StackIndex const idx_, std::string_view const& name_)
{
auto const _absIdx{ luaG_absindex(L_, idx_) };
luaG_pushstring(L_, name_); // L_: ... t ... name_
lua_rawget(L_, _absIdx); // L_: ... t ... <field>
return luaG_type(L_, kIdxTop);
}
// #################################################################################################
template <size_t N>
static inline void luaG_newlib(lua_State* const L_, luaL_Reg const (&funcs_)[N])
{
lua_createtable(L_, 0, N - 1);
luaG_registerlibfuncs(L_, funcs_);
}
// #################################################################################################
template <typename T>
[[nodiscard]]
T* luaG_newuserdatauv(lua_State* const L_, UserValueCount const nuvalue_)
{
return static_cast<T*>(lua_newuserdatauv(L_, sizeof(T), nuvalue_));
}
// #################################################################################################
inline void luaG_pushglobaltable(lua_State* const L_)
{
#ifdef LUA_GLOBALSINDEX // All flavors of Lua 5.1
::lua_pushvalue(L_, LUA_GLOBALSINDEX);
#else // LUA_GLOBALSINDEX
::lua_rawgeti(L_, kIdxRegistry, LUA_RIDX_GLOBALS);
#endif // LUA_GLOBALSINDEX
}
// #################################################################################################
inline void luaG_setfield(lua_State* const L_, StackIndex const idx_, char const* const k_) = delete;
inline void luaG_setfield(lua_State* const L_, StackIndex const idx_, std::string_view const& k_)
{
lua_setfield(L_, idx_, k_.data());
}
// #################################################################################################
inline void luaG_setmetatable(lua_State* const L_, std::string_view const& tname_)
{
// fake externs to make clang happy...
if constexpr (LUA_VERSION_NUM > 501) {
extern void luaL_setmetatable(lua_State* const L_, char const* const tname_); // Lua 5.2+
luaL_setmetatable(L_, tname_.data());
} else {
luaL_getmetatable(L_, tname_.data());
lua_setmetatable(L_, -2);
}
}
// #################################################################################################
// a small helper to extract a full userdata pointer from the stack in a safe way
template <typename T>
[[nodiscard]]
T* luaG_tofulluserdata(lua_State* const L_, StackIndex const index_)
{
LUA_ASSERT(L_, lua_isnil(L_, index_) || lua_type(L_, index_) == LUA_TUSERDATA);
return static_cast<T*>(lua_touserdata(L_, index_));
}
// -------------------------------------------------------------------------------------------------
template <typename T>
[[nodiscard]]
auto luaG_tolightuserdata(lua_State* const L_, StackIndex const index_)
{
LUA_ASSERT(L_, lua_isnil(L_, index_) || lua_islightuserdata(L_, index_));
if constexpr (std::is_pointer_v<T>) {
return static_cast<T>(lua_touserdata(L_, index_));
} else {
return static_cast<T*>(lua_touserdata(L_, index_));
}
}
// -------------------------------------------------------------------------------------------------
[[nodiscard]]
inline std::string_view luaG_typename(lua_State* const L_, LuaType const t_)
{
return lua_typename(L_, static_cast<int>(t_));
}
// -------------------------------------------------------------------------------------------------
[[nodiscard]]
inline std::string_view luaG_typename(lua_State* const L_, StackIndex const idx_)
{
return luaG_typename(L_, luaG_type(L_, idx_));
}
|