aboutsummaryrefslogtreecommitdiff
path: root/src/macros_and_utils.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/macros_and_utils.h220
1 files changed, 153 insertions, 67 deletions
diff --git a/src/macros_and_utils.h b/src/macros_and_utils.h
index e184476..e8d5ab5 100644
--- a/src/macros_and_utils.h
+++ b/src/macros_and_utils.h
@@ -1,107 +1,193 @@
1/* 1#pragma once
2 * MACROS_AND_UTILS.H
3 */
4#ifndef MACROS_AND_UTILS_H
5#define MACROS_AND_UTILS_H
6 2
3#ifdef __cplusplus
4extern "C" {
5#endif // __cplusplus
7#include "lua.h" 6#include "lua.h"
8#include "lualib.h" 7#include "lualib.h"
9#include "lauxlib.h" 8#include "lauxlib.h"
9#ifdef __cplusplus
10}
11#endif // __cplusplus
12
13#include <cassert>
14#include <chrono>
15#include <tuple>
16#include <type_traits>
10 17
11 // M$ compiler doesn't support 'inline' keyword in C files... 18using namespace std::chrono_literals;
12#if defined( _MSC_VER)
13#define inline __inline
14#endif
15 19
16#define USE_DEBUG_SPEW() 0 20#define USE_DEBUG_SPEW() 0
17#if USE_DEBUG_SPEW() 21#if USE_DEBUG_SPEW()
18extern char const* debugspew_indent; 22extern char const* debugspew_indent;
19#define INDENT_BEGIN "%.*s " 23#define INDENT_BEGIN "%.*s "
20#define INDENT_END , (U ? U->debugspew_indent_depth : 0), debugspew_indent 24#define INDENT_END , (U ? U->debugspew_indent_depth.load(std::memory_order_relaxed) : 0), debugspew_indent
21#define DEBUGSPEW_CODE(_code) _code 25#define DEBUGSPEW_CODE(_code) _code
22#define DEBUGSPEW_PARAM_COMMA( param_) param_, 26#define DEBUGSPEW_OR_NOT(a_, b_) a_
27#define DEBUGSPEW_PARAM_COMMA(param_) param_,
23#define DEBUGSPEW_COMMA_PARAM( param_) , param_ 28#define DEBUGSPEW_COMMA_PARAM( param_) , param_
24#else // USE_DEBUG_SPEW() 29#else // USE_DEBUG_SPEW()
25#define DEBUGSPEW_CODE(_code) 30#define DEBUGSPEW_CODE(_code)
26#define DEBUGSPEW_PARAM_COMMA( param_) 31#define DEBUGSPEW_OR_NOT(a_, b_) b_
32#define DEBUGSPEW_PARAM_COMMA(param_)
27#define DEBUGSPEW_COMMA_PARAM( param_) 33#define DEBUGSPEW_COMMA_PARAM( param_)
28#endif // USE_DEBUG_SPEW() 34#endif // USE_DEBUG_SPEW()
29 35
30#ifdef NDEBUG 36#ifdef NDEBUG
31 37
32#define _ASSERT_L(lua,c) //nothing 38#define _ASSERT_L(lua,c) //nothing
33#define STACK_CHECK(L,o) //nothing
34#define STACK_CHECK_ABS(L,o) //nothing
35#define STACK_MID(L,c) //nothing
36#define STACK_END(L,c) //nothing
37#define STACK_DUMP(L) //nothing 39#define STACK_DUMP(L) //nothing
38 40
41#define STACK_CHECK_START_REL(L, offset_)
42#define STACK_CHECK_START_ABS(L, offset_)
43#define STACK_CHECK_RESET_REL(L, offset_)
44#define STACK_CHECK_RESET_ABS(L, offset_)
45#define STACK_CHECK(L, offset_)
46
39#else // NDEBUG 47#else // NDEBUG
40 48
41#define _ASSERT_L( L, cond_) if( (cond_) == 0) { (void) luaL_error( L, "ASSERT failed: %s:%d '%s'", __FILE__, __LINE__, #cond_);} 49#define _ASSERT_L(L, cond_) if( (cond_) == 0) { (void) luaL_error(L, "ASSERT failed: %s:%d '%s'", __FILE__, __LINE__, #cond_);}
42 50#define STACK_DUMP(L) luaG_dump(L)
43#define STACK_CHECK( L, offset_) \ 51
44 { \ 52class StackChecker
45 int const L##_delta = offset_; \ 53{
46 if( (L##_delta < 0) || (lua_gettop( L) < L##_delta)) \ 54 private:
47 { \ 55 lua_State* const m_L;
48 assert( FALSE); \ 56 int m_oldtop;
49 (void) luaL_error( L, "STACK INIT ASSERT failed (%d not %d): %s:%d", lua_gettop( L), L##_delta, __FILE__, __LINE__); \ 57
50 } \ 58 public:
51 int const L##_oldtop = lua_gettop( L) - L##_delta 59 struct Relative
52 60 {
53#define STACK_CHECK_ABS( L, offset_) \ 61 int const m_offset;
54 { \ 62
55 int const L##_pos = offset_; \ 63 operator int() const { return m_offset; }
56 if( lua_gettop( L) < L##_pos) \ 64 };
57 { \ 65
58 assert( FALSE); \ 66 struct Absolute
59 (void) luaL_error( L, "STACK INIT ASSERT failed (%d not %d): %s:%d", lua_gettop( L), L##_pos, __FILE__, __LINE__); \ 67 {
60 } \ 68 int const m_offset;
61 int const L##_oldtop = 0 69
62 70 operator int() const { return m_offset; }
63#define STACK_MID( L, change) \ 71 };
64 do if( change != LUA_MULTRET) \ 72
65 { \ 73 StackChecker(lua_State* const L_, Relative offset_, char const* file_, size_t const line_)
66 int stack_check_a = lua_gettop( L) - L##_oldtop; \ 74 : m_L{ L_ }
67 int stack_check_b = (change); \ 75 , m_oldtop{ lua_gettop(L_) - offset_ }
68 if( stack_check_a != stack_check_b) \ 76 {
69 { \ 77 if ((offset_ < 0) || (m_oldtop < 0))
70 assert( FALSE); \ 78 {
71 luaL_error( L, "STACK ASSERT failed (%d not %d): %s:%d", stack_check_a, stack_check_b, __FILE__, __LINE__); \ 79 assert(false);
72 } \ 80 luaL_error(m_L, "STACK INIT ASSERT failed (%d not %d): %s:%d", lua_gettop(m_L), offset_, file_, line_); // doesn't return
73 } while( 0) 81 }
74 82 }
75#define STACK_END( L, change) \ 83
76 STACK_MID( L, change); \ 84 StackChecker(lua_State* const L_, Absolute pos_, char const* file_, size_t const line_)
85 : m_L{ L_ }
86 , m_oldtop{ 0 }
87 {
88 if (lua_gettop(m_L) != pos_)
89 {
90 assert(false);
91 luaL_error(m_L, "STACK INIT ASSERT failed (%d not %d): %s:%d", lua_gettop(m_L), pos_, file_, line_); // doesn't return
92 }
77 } 93 }
78 94
79#define STACK_DUMP( L) luaG_dump( L) 95 StackChecker& operator=(StackChecker const& rhs_)
96 {
97 assert(m_L == rhs_.m_L);
98 m_oldtop = rhs_.m_oldtop;
99 return *this;
100 }
101
102 // verify if the distance between the current top and the initial one is what we expect
103 void check(int expected_, char const* file_, size_t const line_)
104 {
105 if (expected_ != LUA_MULTRET)
106 {
107 int const actual{ lua_gettop(m_L) - m_oldtop };
108 if (actual != expected_)
109 {
110 assert(false);
111 luaL_error(m_L, "STACK ASSERT failed (%d not %d): %s:%d", actual, expected_, file_, line_); // doesn't return
112 }
113 }
114 }
115};
116
117#define STACK_CHECK_START_REL(L, offset_) StackChecker stackChecker_##L(L, StackChecker::Relative{ offset_ }, __FILE__, __LINE__)
118#define STACK_CHECK_START_ABS(L, offset_) StackChecker stackChecker_##L(L, StackChecker::Absolute{ offset_ }, __FILE__, __LINE__)
119#define STACK_CHECK_RESET_REL(L, offset_) stackChecker_##L = StackChecker{L, StackChecker::Relative{ offset_ }, __FILE__, __LINE__}
120#define STACK_CHECK_RESET_ABS(L, offset_) stackChecker_##L = StackChecker{L, StackChecker::Absolute{ offset_ }, __FILE__, __LINE__}
121#define STACK_CHECK(L, offset_) stackChecker_##L.check(offset_, __FILE__, __LINE__)
80 122
81#endif // NDEBUG 123#endif // NDEBUG
82 124
83#define ASSERT_L(c) _ASSERT_L(L,c) 125#define ASSERT_L(c) _ASSERT_L(L,c)
84 126
85inline void STACK_GROW(lua_State * L, int n_) 127inline void STACK_GROW(lua_State* L, int n_)
86{ 128{
87 if (!lua_checkstack(L, n_)) 129 if (!lua_checkstack(L, n_))
88 luaL_error(L, "Cannot grow stack!"); 130 {
131 luaL_error(L, "Cannot grow stack!"); // doesn't return
132 }
89} 133}
90 134
91// non-string keyed registry access 135#define LUAG_FUNC(func_name) [[nodiscard]] int LG_##func_name(lua_State* L)
92#define REGISTRY_SET( L, key_, value_) \ 136
93{ \ 137// #################################################################################################
94 push_unique_key( L, key_); \ 138
95 value_; \ 139// a small helper to extract a full userdata pointer from the stack in a safe way
96 lua_rawset( L, LUA_REGISTRYINDEX); \ 140template<typename T>
141[[nodiscard]] T* lua_tofulluserdata(lua_State* L, int index_)
142{
143 ASSERT_L(lua_isnil(L, index_) || lua_type(L, index_) == LUA_TUSERDATA);
144 return static_cast<T*>(lua_touserdata(L, index_));
97} 145}
98 146
99#define REGISTRY_GET( L, key_) \ 147template<typename T>
100{ \ 148[[nodiscard]] auto lua_tolightuserdata(lua_State* L, int index_)
101 push_unique_key( L, key_); \ 149{
102 lua_rawget( L, LUA_REGISTRYINDEX); \ 150 ASSERT_L(lua_isnil(L, index_) || lua_islightuserdata(L, index_));
151 if constexpr (std::is_pointer_v<T>)
152 {
153 return static_cast<T>(lua_touserdata(L, index_));
154 }
155 else
156 {
157 return static_cast<T*>(lua_touserdata(L, index_));
158 }
159}
160
161template <typename T>
162[[nodiscard]] T* lua_newuserdatauv(lua_State* L, int nuvalue_)
163{
164 return static_cast<T*>(lua_newuserdatauv(L, sizeof(T), nuvalue_));
165}
166
167// #################################################################################################
168
169// use this instead of Lua's lua_error if possible
170[[noreturn]] static inline void raise_lua_error(lua_State* L)
171{
172 std::ignore = lua_error(L); // doesn't return
173 assert(false); // we should never get here, but i'm paranoid
103} 174}
104 175
105#define LUAG_FUNC( func_name) int LG_##func_name( lua_State* L) 176using lua_Duration = std::chrono::template duration<lua_Number>;
177
178// #################################################################################################
179
180// A unique type generator
181template <typename T, auto = []{}>
182struct Unique
183{
184 T m_val;
185 constexpr Unique() = default;
186 constexpr operator T() const { return m_val; }
187 constexpr explicit Unique(T b_) : m_val{ b_ } {}
188};
189
190// #################################################################################################
106 191
107#endif // MACROS_AND_UTILS_H 192using Source = Unique<lua_State*>;
193using Dest = Unique<lua_State*>; \ No newline at end of file