Etterna 0.74.4
Loading...
Searching...
No Matches
LuaBinding.h
1/* LuaBinding - helpers to expose Lua bindings for C++ classes. */
2
3#ifndef LuaBinding_H
4#define LuaBinding_H
5
6#include "Etterna/Singletons/LuaManager.h"
7class LuaReference;
8
10{
11 public:
12 LuaBinding();
13 virtual ~LuaBinding();
14 void Register(lua_State* L);
15
16 static void RegisterTypes(lua_State* L);
17
18 [[nodiscard]] auto IsDerivedClass() const -> bool
19 {
20 return GetClassName() != GetBaseClassName();
21 }
22 [[nodiscard]] virtual auto GetClassName() const -> const std::string& = 0;
23 [[nodiscard]] virtual auto GetBaseClassName() const
24 -> const std::string& = 0;
25
26 static void ApplyDerivedType(Lua* L,
27 const std::string& sClassname,
28 void* pSelf);
29 static auto CheckLuaObjectType(lua_State* L,
30 int narg,
31 std::string const& szType) -> bool;
32
33 protected:
34 virtual void Register(Lua* L, int iMethods, int iMetatable) = 0;
35
36 static void CreateMethodsTable(lua_State* L, const std::string& szName);
37 static auto GetPointerFromStack(Lua* L, const std::string& sType, int iArg)
38 -> void*;
39
40 static auto Equal(lua_State* L) -> bool;
41 static auto PushEqual(lua_State* L) -> int;
42};
43
45template<typename Type>
46class Luna : public LuaBinding
47{
48 protected:
49 using T = Type;
50 using binding_t = int(T*, lua_State*);
51
52 struct RegType
53 {
54 std::string regName;
55 binding_t* mfunc;
56 };
57
58 void Register(Lua* L, int iMethods, int iMetatable) override
59 {
60 lua_pushcfunction(L, tostring_T);
61 lua_setfield(L, iMetatable, "__tostring");
62
63 // fill method table with methods from class T
64 for (auto const& m : m_aMethods) {
65 lua_pushlightuserdata(L, reinterpret_cast<void*>(m.mfunc));
66 lua_pushcclosure(L, thunk, 1);
67 lua_setfield(L, iMethods, m.regName.c_str());
68 }
69 }
70
71 public:
72 [[nodiscard]] auto GetClassName() const -> const std::string& override
73 {
74 return m_sClassName;
75 }
76 [[nodiscard]] auto GetBaseClassName() const -> const std::string& override
77 {
78 return m_sBaseClassName;
79 }
80 static std::string m_sClassName;
81 static std::string m_sBaseClassName;
82
83 // Get userdata from the Lua stack and return a pointer to T object.
84 static auto check(lua_State* L, int narg, bool bIsSelf = false) -> T*
85 {
86 if (!LuaBinding::CheckLuaObjectType(L, narg, m_sClassName)) {
87 if (bIsSelf) {
88 luaL_typerror(L, narg, m_sClassName.c_str());
89 } else {
90 LuaHelpers::TypeError(L, narg, m_sClassName);
91 }
92 }
93 return get(L, narg);
94 }
95
96 static auto get(lua_State* L, int narg) -> T*
97 {
98 return reinterpret_cast<T*>(GetPointerFromStack(L, m_sClassName, narg));
99 }
100
101 /* Push a table or userdata for the given object. This is called on the
102 * base class, so we pick up the instance of the base class, if any. */
103 static void PushObject(Lua* L, const std::string& sDerivedClassName, T* p);
104
105 protected:
106 void AddMethod(std::string const& regName, int (*pFunc)(T* p, lua_State* L))
107 {
108 RegType r = { regName, pFunc };
109 m_aMethods.push_back(r);
110 }
111
112 private:
113 // member function dispatcher
114 static auto thunk(Lua* L) -> int
115 {
116 // stack has userdata, followed by method args
117 T* obj = check(L, 1, true); // get self
118 lua_remove(L,
119 1); // remove self so member function args start at index 1
120 // get member function from upvalue
121 auto* pFunc = reinterpret_cast<binding_t*>(lua_touserdata(L, lua_upvalueindex(1)));
122 return pFunc(obj, L); // call member function
123 }
124
125 std::vector<RegType> m_aMethods;
126
127 static auto tostring_T(lua_State* L) -> int
128 {
129 char buff[32];
130 const void* pData = check(L, 1);
131 snprintf(buff, sizeof(buff), "%p", pData);
132 lua_pushfstring(L, "%s (%s)", m_sClassName.c_str(), buff);
133 return 1;
134 }
135};
136
137/*
138 * Instanced classes have an associated table, which is used as "self"
139 * instead of a raw userdata. This should be as lightweight as possible.
140 */
141#include "LuaReference.h"
142class LuaClass : public LuaTable
143{
144 public:
145 LuaClass() = default;
146 LuaClass(const LuaClass& cpy);
147 ~LuaClass() override;
148 auto operator=(const LuaClass& cpy) -> LuaClass&;
149};
150
151/* Only a base class has to indicate that it's instanced (has a per-object
152 * Lua table). Derived classes simply call the base class's Push function,
153 * specifying a different class name, so they don't need to know about it. */
154#define LUA_REGISTER_INSTANCED_BASE_CLASS(T) \
155 template<> \
156 void Luna<T>::PushObject( \
157 Lua* L, const std::string& sDerivedClassName, T* p) \
158 { \
159 p->m_pLuaInstance->PushSelf(L); \
160 LuaBinding::ApplyDerivedType(L, sDerivedClassName, p); \
161 } \
162 LUA_REGISTER_CLASS_BASIC(T, T)
163
164#define LUA_REGISTER_CLASS(T) \
165 template<> \
166 void Luna<T>::PushObject( \
167 Lua* L, const std::string& sDerivedClassName, T* p) \
168 { \
169 void** pData = (void**)lua_newuserdata(L, sizeof(void*)); \
170 *pData = p; \
171 LuaBinding::ApplyDerivedType(L, sDerivedClassName, p); \
172 } \
173 LUA_REGISTER_CLASS_BASIC(T, T)
174
175#define LUA_REGISTER_DERIVED_CLASS(T, B) \
176 template<> \
177 void Luna<T>::PushObject( \
178 Lua* L, const std::string& sDerivedClassName, T* p) \
179 { \
180 Luna<B>::PushObject(L, sDerivedClassName, p); \
181 } \
182 LUA_REGISTER_CLASS_BASIC(T, B)
183
184#define LUA_REGISTER_CLASS_BASIC(T, B) \
185 template<> \
186 std::string Luna<T>::m_sClassName = #T; \
187 template<> \
188 std::string Luna<T>::m_sBaseClassName = #B; \
189 void T::PushSelf(lua_State* L) \
190 { \
191 Luna<B>::PushObject(L, Luna<T>::m_sClassName, this); \
192 } \
193 static Luna##T registera##T; \
194 /* Call PushSelf, so we always call the derived Luna<T>::Push. */ \
195 namespace LuaHelpers { \
196 template<> \
197 void Push<T*>(lua_State * L, T* const& pObject) \
198 { \
199 if (pObject == nullptr) \
200 lua_pushnil(L); \
201 else \
202 pObject->PushSelf(L); \
203 } \
204 }
205
206#define DEFINE_METHOD(method_name, expr) \
207 static int method_name(T* p, lua_State* L) \
208 { \
209 LuaHelpers::Push(L, p->expr); \
210 return 1; \
211 }
212
213#define COMMON_RETURN_SELF \
214 p->PushSelf(L); \
215 return 1;
216
217#define GET_SET_BOOL_METHOD(method_name, bool_name) \
218 \
219 static int get_##method_name(T* p, lua_State* L) \
220 \
221 { \
222 lua_pushboolean(L, p->bool_name); \
223 return 1; \
224 } \
225 \
226 static int set_##method_name(T* p, lua_State* L) \
227 \
228 { \
229 p->bool_name = lua_toboolean(L, 1) != 0; \
230 COMMON_RETURN_SELF; \
231 }
232
233#define GETTER_SETTER_BOOL_METHOD(bool_name) \
234 \
235 static int get_##bool_name(T* p, lua_State* L) \
236 \
237 { \
238 lua_pushboolean(L, p->get_##bool_name()); \
239 return 1; \
240 } \
241 \
242 static int set_##bool_name(T* p, lua_State* L) \
243 \
244 { \
245 p->set_##bool_name(lua_toboolean(L, 1) != 0); \
246 COMMON_RETURN_SELF; \
247 }
248
249#define GET_SET_FLOAT_METHOD(method_name, float_name) \
250 \
251 static int get_##method_name(T* p, lua_State* L) \
252 \
253 { \
254 lua_pushnumber(L, p->float_name); \
255 return 1; \
256 } \
257 \
258 static int set_##method_name(T* p, lua_State* L) \
259 \
260 { \
261 p->float_name = FArg(1); \
262 COMMON_RETURN_SELF; \
263 }
264
265#define GET_SET_INT_METHOD(method_name, int_name) \
266 \
267 static int get_##method_name(T* p, lua_State* L) \
268 \
269 { \
270 lua_pushinteger(L, p->int_name); \
271 return 1; \
272 } \
273 \
274 static int set_##method_name(T* p, lua_State* L) \
275 \
276 { \
277 p->int_name = IArg(1); \
278 COMMON_RETURN_SELF; \
279 }
280
281#define GETTER_SETTER_FLOAT_METHOD(float_name) \
282 \
283 static int get_##float_name(T* p, lua_State* L) \
284 \
285 { \
286 lua_pushnumber(L, p->get_##float_name()); \
287 return 1; \
288 } \
289 \
290 static int set_##float_name(T* p, lua_State* L) \
291 \
292 { \
293 p->set_##float_name(FArg(1)); \
294 COMMON_RETURN_SELF; \
295 }
296
297#define GET_SET_ENUM_METHOD(method_name, enum_name, val_name) \
298 \
299 static int get_##method_name(T* p, lua_State* L) \
300 \
301 { \
302 Enum::Push(L, p->val_name); \
303 return 1; \
304 } \
305 \
306 static int set_##method_name(T* p, lua_State* L) \
307 \
308 { \
309 p->val_name = Enum::Check<enum_name>(L, 1); \
310 COMMON_RETURN_SELF; \
311 }
312
313#define GETTER_SETTER_ENUM_METHOD(enum_name, val_name) \
314 \
315 static int get_##val_name(T* p, lua_State* L) \
316 \
317 { \
318 Enum::Push(L, p->get_##val_name()); \
319 return 1; \
320 } \
321 \
322 static int set_##val_name(T* p, lua_State* L) \
323 \
324 { \
325 p->set_##val_name(Enum::Check<enum_name>(L, 1)); \
326 COMMON_RETURN_SELF; \
327 }
328
329#define ADD_METHOD(method_name) AddMethod(#method_name, method_name)
330#define ADD_GET_SET_METHODS(method_name) \
331 ADD_METHOD(get_##method_name); \
332 ADD_METHOD(set_##method_name);
333#define LUA_SET_MEMBER(member, arg_conv) \
334 \
335 static int set_##member(T* p, lua_State* L) \
336 \
337 { \
338 p->m_##member = arg_conv(1); \
339 COMMON_RETURN_SELF; \
340 }
341#define GET_SET_MEMBER(member, arg_conv) \
342 \
343 DEFINE_METHOD(get_##member, m_##member); \
344 \
345 LUA_SET_MEMBER(member, arg_conv);
346
347#define LUA_REGISTER_NAMESPACE(T) \
348 static void Register##T(lua_State* L) \
349 { \
350 luaL_register(L, #T, T##Table); \
351 lua_pop(L, 1); \
352 } \
353 REGISTER_WITH_LUA_FUNCTION(Register##T)
354#define LIST_METHOD(method_name) \
355 { \
356#method_name, method_name \
357 }
358
359// Explicitly separates the stack into args and return values.
360// This way, the stack can safely be used to store the previous values.
361void
362DefaultNilArgs(lua_State* L, int n);
363auto
364FArgGTEZero(lua_State* L, int index) -> float;
365
366#endif
Definition LuaBinding.h:10
Definition LuaBinding.h:143
A self-cleaning Lua reference.
Definition LuaReference.h:10
Definition LuaReference.h:105
Allow the binding of Lua to various classes.
Definition LuaBinding.h:47
Definition LuaBinding.h:53