OpenTTD Source 20250205-master-gfd85ab1e2c
squirrel_helper.hpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
7
10#ifndef SQUIRREL_HELPER_HPP
11#define SQUIRREL_HELPER_HPP
12
13#include "squirrel.hpp"
14#include "../string_func.h"
15#include "../tile_type.h"
16#include "../core/convertible_through_base.hpp"
18
19template <class CL, ScriptType ST> const char *GetClassName();
20
24namespace SQConvert {
29 template <typename T> struct Return;
30
31 template <> struct Return<uint8_t> { static inline int Set(HSQUIRRELVM vm, uint8_t res) { sq_pushinteger(vm, (int32_t)res); return 1; } };
32 template <> struct Return<uint16_t> { static inline int Set(HSQUIRRELVM vm, uint16_t res) { sq_pushinteger(vm, (int32_t)res); return 1; } };
33 template <> struct Return<uint32_t> { static inline int Set(HSQUIRRELVM vm, uint32_t res) { sq_pushinteger(vm, (int32_t)res); return 1; } };
34 template <> struct Return<int8_t> { static inline int Set(HSQUIRRELVM vm, int8_t res) { sq_pushinteger(vm, res); return 1; } };
35 template <> struct Return<int16_t> { static inline int Set(HSQUIRRELVM vm, int16_t res) { sq_pushinteger(vm, res); return 1; } };
36 template <> struct Return<int32_t> { static inline int Set(HSQUIRRELVM vm, int32_t res) { sq_pushinteger(vm, res); return 1; } };
37 template <> struct Return<int64_t> { static inline int Set(HSQUIRRELVM vm, int64_t res) { sq_pushinteger(vm, res); return 1; } };
38 template <> struct Return<TileIndex> { static inline int Set(HSQUIRRELVM vm, TileIndex res) { sq_pushinteger(vm, (int32_t)res.base()); return 1; } };
39 template <> struct Return<bool> { static inline int Set(HSQUIRRELVM vm, bool res) { sq_pushbool (vm, res); return 1; } };
40 template <> struct Return<char *> { /* Do not use char *, use std::optional<std::string> instead. */ };
41 template <> struct Return<const char *> { /* Do not use const char *, use std::optional<std::string> instead. */ };
42 template <> struct Return<void *> { static inline int Set(HSQUIRRELVM vm, void *res) { sq_pushuserpointer(vm, res); return 1; } };
43 template <> struct Return<HSQOBJECT> { static inline int Set(HSQUIRRELVM vm, HSQOBJECT res) { sq_pushobject(vm, res); return 1; } };
44
45 template <typename T> requires std::is_enum_v<T> struct Return<T> {
46 static inline int Set(HSQUIRRELVM vm, T res)
47 {
48 sq_pushinteger(vm, to_underlying(res));
49 return 1;
50 }
51 };
52
53 template <ConvertibleThroughBase T> struct Return<T> {
54 static inline int Set(HSQUIRRELVM vm, T res)
55 {
56 sq_pushinteger(vm, res.base());
57 return 1;
58 }
59 };
60
61 template <> struct Return<std::optional<std::string>> {
62 static inline int Set(HSQUIRRELVM vm, std::optional<std::string> res)
63 {
64 if (res.has_value()) {
65 sq_pushstring(vm, res.value(), -1);
66 } else {
67 sq_pushnull(vm);
68 }
69 return 1;
70 }
71 };
72
77 template <typename T> struct Param;
78
79 template <> struct Param<uint8_t> { static inline uint8_t Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } };
80 template <> struct Param<uint16_t> { static inline uint16_t Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } };
81 template <> struct Param<uint32_t> { static inline uint32_t Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } };
82 template <> struct Param<int8_t> { static inline int8_t Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } };
83 template <> struct Param<int16_t> { static inline int16_t Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } };
84 template <> struct Param<int32_t> { static inline int32_t Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } };
85 template <> struct Param<int64_t> { static inline int64_t Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } };
86 template <> struct Param<TileIndex> { static inline TileIndex Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return TileIndex((uint32_t)(int32_t)tmp); } };
87 template <> struct Param<bool> { static inline bool Get(HSQUIRRELVM vm, int index) { SQBool tmp; sq_getbool (vm, index, &tmp); return tmp != 0; } };
88 template <> struct Param<const char *> { /* Do not use const char *, use std::string& instead. */ };
89 template <> struct Param<void *> { static inline void *Get(HSQUIRRELVM vm, int index) { SQUserPointer tmp; sq_getuserpointer(vm, index, &tmp); return tmp; } };
90
91 template <typename T> requires std::is_enum_v<T> struct Param<T> {
92 static inline T Get(HSQUIRRELVM vm, int index)
93 {
94 SQInteger tmp;
95 sq_getinteger(vm, index, &tmp);
96 return static_cast<T>(tmp);
97 }
98 };
99
100 template <ConvertibleThroughBase T> struct Param<T> {
101 static inline T Get(HSQUIRRELVM vm, int index)
102 {
103 SQInteger tmp;
104 sq_getinteger(vm, index, &tmp);
105 return T{static_cast<T::BaseType>(tmp)};
106 }
107 };
108
109 template <> struct Param<const std::string &> {
110 static inline const std::string Get(HSQUIRRELVM vm, int index)
111 {
112 /* Convert what-ever there is as parameter to a string */
113 sq_tostring(vm, index);
114
115 const SQChar *tmp;
116 sq_getstring(vm, -1, &tmp);
117 std::string result = StrMakeValid(tmp);
118 sq_poptop(vm);
119 return result;
120 }
121 };
122
123 template <typename Titem>
124 struct Param<Array<Titem> &&> {
125 static inline Array<Titem> Get(HSQUIRRELVM vm, int index)
126 {
127 /* Sanity check of the size. */
128 if (sq_getsize(vm, index) > UINT16_MAX) throw sq_throwerror(vm, "an array used as parameter to a function is too large");
129
130 SQObject obj;
131 sq_getstackobj(vm, index, &obj);
132 sq_pushobject(vm, obj);
133 sq_pushnull(vm);
134
135 Array<Titem> data;
136
137 while (SQ_SUCCEEDED(sq_next(vm, -2))) {
138 data.emplace_back(Param<Titem>::Get(vm, -1));
139 sq_pop(vm, 2);
140 }
141 sq_pop(vm, 2);
142
143 return data;
144 }
145 };
146
152 template <typename Tfunc> struct HelperT;
153
157 template <typename Tretval, typename... Targs>
158 struct HelperT<Tretval (*)(Targs...)> {
159 static int SQCall(void *instance, Tretval(*func)(Targs...), HSQUIRRELVM vm)
160 {
161 return SQCall(instance, func, vm, std::index_sequence_for<Targs...>{});
162 }
163
164 private:
165 template <size_t... i>
166 static int SQCall(void *, Tretval(*func)(Targs...), [[maybe_unused]] HSQUIRRELVM vm, std::index_sequence<i...>)
167 {
168 if constexpr (std::is_void_v<Tretval>) {
169 (*func)(
170 Param<Targs>::Get(vm, 2 + i)...
171 );
172 return 0;
173 } else {
174 Tretval ret = (*func)(
175 Param<Targs>::Get(vm, 2 + i)...
176 );
177 return Return<Tretval>::Set(vm, ret);
178 }
179 }
180 };
181
185 template <class Tcls, typename Tretval, typename... Targs>
186 struct HelperT<Tretval(Tcls:: *)(Targs...)> {
187 static int SQCall(Tcls *instance, Tretval(Tcls:: *func)(Targs...), HSQUIRRELVM vm)
188 {
189 return SQCall(instance, func, vm, std::index_sequence_for<Targs...>{});
190 }
191
192 static Tcls *SQConstruct(Tcls *instance, Tretval(Tcls:: *func)(Targs...), HSQUIRRELVM vm)
193 {
194 return SQConstruct(instance, func, vm, std::index_sequence_for<Targs...>{});
195 }
196
197 private:
198 template <size_t... i>
199 static int SQCall(Tcls *instance, Tretval(Tcls:: *func)(Targs...), [[maybe_unused]] HSQUIRRELVM vm, std::index_sequence<i...>)
200 {
201 if constexpr (std::is_void_v<Tretval>) {
202 (instance->*func)(
203 Param<Targs>::Get(vm, 2 + i)...
204 );
205 return 0;
206 } else {
207 Tretval ret = (instance->*func)(
208 Param<Targs>::Get(vm, 2 + i)...
209 );
210 return Return<Tretval>::Set(vm, ret);
211 }
212 }
213
214 template <size_t... i>
215 static Tcls *SQConstruct(Tcls *, Tretval(Tcls:: *)(Targs...), [[maybe_unused]] HSQUIRRELVM vm, std::index_sequence<i...>)
216 {
217 Tcls *inst = new Tcls(
218 Param<Targs>::Get(vm, 2 + i)...
219 );
220
221 return inst;
222 }
223 };
224
225
231 template <typename Tcls, typename Tmethod, ScriptType Ttype>
232 inline SQInteger DefSQNonStaticCallback(HSQUIRRELVM vm)
233 {
234 /* Find the amount of params we got */
235 int nparam = sq_gettop(vm);
236 SQUserPointer ptr = nullptr;
237 SQUserPointer real_instance = nullptr;
238 HSQOBJECT instance;
239
240 /* Get the 'SQ' instance of this class */
241 Squirrel::GetInstance(vm, &instance);
242
243 /* Protect against calls to a non-static method in a static way */
244 sq_pushroottable(vm);
245 const char *className = GetClassName<Tcls, Ttype>();
246 sq_pushstring(vm, className, -1);
247 sq_get(vm, -2);
248 sq_pushobject(vm, instance);
249 if (sq_instanceof(vm) != SQTrue) return sq_throwerror(vm, "class method is non-static");
250 sq_pop(vm, 3);
251
252 /* Get the 'real' instance of this class */
253 sq_getinstanceup(vm, 1, &real_instance, nullptr);
254 /* Get the real function pointer */
255 sq_getuserdata(vm, nparam, &ptr, nullptr);
256 if (real_instance == nullptr) return sq_throwerror(vm, "couldn't detect real instance of class for non-static call");
257 /* Remove the userdata from the stack */
258 sq_pop(vm, 1);
259
260 try {
261 /* Delegate it to a template that can handle this specific function */
262 return HelperT<Tmethod>::SQCall((Tcls *)real_instance, *(Tmethod *)ptr, vm);
263 } catch (SQInteger &e) {
264 return e;
265 }
266 }
267
273 template <typename Tcls, typename Tmethod, ScriptType Ttype>
274 inline SQInteger DefSQAdvancedNonStaticCallback(HSQUIRRELVM vm)
275 {
276 /* Find the amount of params we got */
277 int nparam = sq_gettop(vm);
278 SQUserPointer ptr = nullptr;
279 SQUserPointer real_instance = nullptr;
280 HSQOBJECT instance;
281
282 /* Get the 'SQ' instance of this class */
283 Squirrel::GetInstance(vm, &instance);
284
285 /* Protect against calls to a non-static method in a static way */
286 sq_pushroottable(vm);
287 const char *className = GetClassName<Tcls, Ttype>();
288 sq_pushstring(vm, className, -1);
289 sq_get(vm, -2);
290 sq_pushobject(vm, instance);
291 if (sq_instanceof(vm) != SQTrue) return sq_throwerror(vm, "class method is non-static");
292 sq_pop(vm, 3);
293
294 /* Get the 'real' instance of this class */
295 sq_getinstanceup(vm, 1, &real_instance, nullptr);
296 /* Get the real function pointer */
297 sq_getuserdata(vm, nparam, &ptr, nullptr);
298 if (real_instance == nullptr) return sq_throwerror(vm, "couldn't detect real instance of class for non-static call");
299 /* Remove the userdata from the stack */
300 sq_pop(vm, 1);
301
302 /* Call the function, which its only param is always the VM */
303 return (SQInteger)(((Tcls *)real_instance)->*(*(Tmethod *)ptr))(vm);
304 }
305
311 template <typename Tcls, typename Tmethod>
312 inline SQInteger DefSQStaticCallback(HSQUIRRELVM vm)
313 {
314 /* Find the amount of params we got */
315 int nparam = sq_gettop(vm);
316 SQUserPointer ptr = nullptr;
317
318 /* Get the real function pointer */
319 sq_getuserdata(vm, nparam, &ptr, nullptr);
320
321 try {
322 /* Delegate it to a template that can handle this specific function */
323 return HelperT<Tmethod>::SQCall((Tcls *)nullptr, *(Tmethod *)ptr, vm);
324 } catch (SQInteger &e) {
325 return e;
326 }
327 }
328
329
335 template <typename Tcls, typename Tmethod>
336 inline SQInteger DefSQAdvancedStaticCallback(HSQUIRRELVM vm)
337 {
338 /* Find the amount of params we got */
339 int nparam = sq_gettop(vm);
340 SQUserPointer ptr = nullptr;
341
342 /* Get the real function pointer */
343 sq_getuserdata(vm, nparam, &ptr, nullptr);
344 /* Remove the userdata from the stack */
345 sq_pop(vm, 1);
346
347 /* Call the function, which its only param is always the VM */
348 return (SQInteger)(*(*(Tmethod *)ptr))(vm);
349 }
350
355 template <typename Tcls>
356 static SQInteger DefSQDestructorCallback(SQUserPointer p, SQInteger)
357 {
358 /* Remove the real instance too */
359 if (p != nullptr) ((Tcls *)p)->Release();
360 return 0;
361 }
362
368 template <typename Tcls, typename Tmethod, int Tnparam>
369 inline SQInteger DefSQConstructorCallback(HSQUIRRELVM vm)
370 {
371 try {
372 /* Create the real instance */
373 Tcls *instance = HelperT<Tmethod>::SQConstruct((Tcls *)nullptr, (Tmethod)nullptr, vm);
374 sq_setinstanceup(vm, -Tnparam, instance);
375 sq_setreleasehook(vm, -Tnparam, DefSQDestructorCallback<Tcls>);
376 instance->AddRef();
377 return 0;
378 } catch (SQInteger &e) {
379 return e;
380 }
381 }
382
387 template <typename Tcls>
388 inline SQInteger DefSQAdvancedConstructorCallback(HSQUIRRELVM vm)
389 {
390 try {
391 /* Find the amount of params we got */
392 int nparam = sq_gettop(vm);
393
394 /* Create the real instance */
395 Tcls *instance = new Tcls(vm);
396 sq_setinstanceup(vm, -nparam, instance);
397 sq_setreleasehook(vm, -nparam, DefSQDestructorCallback<Tcls>);
398 instance->AddRef();
399 return 0;
400 } catch (SQInteger &e) {
401 return e;
402 }
403 }
404
405} // namespace SQConvert
406
407#endif /* SQUIRREL_HELPER_HPP */
static bool GetInstance(HSQUIRRELVM vm, HSQOBJECT *ptr, int pos=1)
Get the Squirrel-instance pointer.
Definition squirrel.hpp:200
constexpr std::underlying_type_t< enum_type > to_underlying(enum_type e)
Implementation of std::to_underlying (from C++23)
Definition enum_type.hpp:15
The Squirrel convert routines.
SQInteger DefSQAdvancedConstructorCallback(HSQUIRRELVM vm)
A general template to handle creating of an instance with a complex constructor.
static SQInteger DefSQDestructorCallback(SQUserPointer p, SQInteger)
A general template for the destructor of SQ instances.
SQInteger DefSQAdvancedStaticCallback(HSQUIRRELVM vm)
A general template for all static advanced method callbacks from Squirrel.
SQInteger DefSQConstructorCallback(HSQUIRRELVM vm)
A general template to handle creating of instance with any amount of params.
SQInteger DefSQAdvancedNonStaticCallback(HSQUIRRELVM vm)
A general template for all non-static advanced method callbacks from Squirrel.
SQInteger DefSQNonStaticCallback(HSQUIRRELVM vm)
A general template for all non-static method callbacks from Squirrel.
SQInteger DefSQStaticCallback(HSQUIRRELVM vm)
A general template for all function/static method callbacks from Squirrel.
defines the Squirrel class
Helper structs for converting Squirrel data structures to C++.
std::vector< Titem > Array
Definition of a simple array.
static void StrMakeValid(T &dst, const char *str, const char *last, StringValidationSettings settings)
Copies the valid (UTF-8) characters from str up to last to the dst.
Definition string.cpp:107
Helper class to recognize the function type (retval type, args) and use the proper specialization for...
To get a param from squirrel, we use this helper class.
To return a value to squirrel, we use this helper class.
StrongType::Typedef< uint32_t, struct TileIndexTag, StrongType::Compare, StrongType::Integer, StrongType::Compatible< int32_t >, StrongType::Compatible< int64_t > > TileIndex
The index/ID of a Tile.
Definition tile_type.h:87