OpenTTD Source 20250312-master-gcdcc6b491d
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> SQInteger PushClassName(HSQUIRRELVM);
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<HSQOBJECT> { static inline int Set(HSQUIRRELVM vm, HSQOBJECT res) { sq_pushobject(vm, res); return 1; } };
43
44 template <typename T> requires std::is_enum_v<T> struct Return<T> {
45 static inline int Set(HSQUIRRELVM vm, T res)
46 {
47 sq_pushinteger(vm, to_underlying(res));
48 return 1;
49 }
50 };
51
52 template <ConvertibleThroughBase T> struct Return<T> {
53 static inline int Set(HSQUIRRELVM vm, T res)
54 {
55 sq_pushinteger(vm, res.base());
56 return 1;
57 }
58 };
59
60 template <> struct Return<std::optional<std::string>> {
61 static inline int Set(HSQUIRRELVM vm, std::optional<std::string> res)
62 {
63 if (res.has_value()) {
64 sq_pushstring(vm, res.value(), -1);
65 } else {
66 sq_pushnull(vm);
67 }
68 return 1;
69 }
70 };
71
76 template <typename T> struct Param;
77
78 template <> struct Param<uint8_t> { static inline uint8_t Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } };
79 template <> struct Param<uint16_t> { static inline uint16_t Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } };
80 template <> struct Param<uint32_t> { static inline uint32_t Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } };
81 template <> struct Param<int8_t> { static inline int8_t Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } };
82 template <> struct Param<int16_t> { static inline int16_t Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } };
83 template <> struct Param<int32_t> { static inline int32_t Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } };
84 template <> struct Param<int64_t> { static inline int64_t Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } };
85 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); } };
86 template <> struct Param<bool> { static inline bool Get(HSQUIRRELVM vm, int index) { SQBool tmp; sq_getbool (vm, index, &tmp); return tmp != 0; } };
87 template <> struct Param<const char *> { /* Do not use const char *, use std::string& instead. */ };
88
89 template <typename T> requires std::is_enum_v<T> struct Param<T> {
90 static inline T Get(HSQUIRRELVM vm, int index)
91 {
92 SQInteger tmp;
93 sq_getinteger(vm, index, &tmp);
94 return static_cast<T>(tmp);
95 }
96 };
97
98 template <ConvertibleThroughBase T> struct Param<T> {
99 static inline T Get(HSQUIRRELVM vm, int index)
100 {
101 SQInteger tmp;
102 sq_getinteger(vm, index, &tmp);
103 return T{static_cast<T::BaseType>(tmp)};
104 }
105 };
106
107 template <> struct Param<const std::string &> {
108 static inline const std::string Get(HSQUIRRELVM vm, int index)
109 {
110 /* Convert what-ever there is as parameter to a string */
111 sq_tostring(vm, index);
112
113 const SQChar *tmp;
114 sq_getstring(vm, -1, &tmp);
115 std::string result = StrMakeValid(tmp);
116 sq_poptop(vm);
117 return result;
118 }
119 };
120
121 template <typename Titem>
122 struct Param<Array<Titem> &&> {
123 static inline Array<Titem> Get(HSQUIRRELVM vm, int index)
124 {
125 /* Sanity check of the size. */
126 if (sq_getsize(vm, index) > UINT16_MAX) throw sq_throwerror(vm, "an array used as parameter to a function is too large");
127
128 SQObject obj;
129 sq_getstackobj(vm, index, &obj);
130 sq_pushobject(vm, obj);
131 sq_pushnull(vm);
132
133 Array<Titem> data;
134
135 while (SQ_SUCCEEDED(sq_next(vm, -2))) {
136 data.emplace_back(Param<Titem>::Get(vm, -1));
137 sq_pop(vm, 2);
138 }
139 sq_pop(vm, 2);
140
141 return data;
142 }
143 };
144
150 template <typename Tfunc> struct HelperT;
151
155 template <typename Tretval, typename... Targs>
156 struct HelperT<Tretval (*)(Targs...)> {
157 static int SQCall(void *instance, Tretval(*func)(Targs...), HSQUIRRELVM vm)
158 {
159 return SQCall(instance, func, vm, std::index_sequence_for<Targs...>{});
160 }
161
162 private:
163 template <size_t... i>
164 static int SQCall(void *, Tretval(*func)(Targs...), [[maybe_unused]] HSQUIRRELVM vm, std::index_sequence<i...>)
165 {
166 if constexpr (std::is_void_v<Tretval>) {
167 (*func)(
168 Param<Targs>::Get(vm, 2 + i)...
169 );
170 return 0;
171 } else {
172 Tretval ret = (*func)(
173 Param<Targs>::Get(vm, 2 + i)...
174 );
175 return Return<Tretval>::Set(vm, ret);
176 }
177 }
178 };
179
183 template <class Tcls, typename Tretval, typename... Targs>
184 struct HelperT<Tretval(Tcls:: *)(Targs...)> {
185 static int SQCall(Tcls *instance, Tretval(Tcls:: *func)(Targs...), HSQUIRRELVM vm)
186 {
187 return SQCall(instance, func, vm, std::index_sequence_for<Targs...>{});
188 }
189
190 static Tcls *SQConstruct(Tcls *instance, Tretval(Tcls:: *func)(Targs...), HSQUIRRELVM vm)
191 {
192 return SQConstruct(instance, func, vm, std::index_sequence_for<Targs...>{});
193 }
194
195 private:
196 template <size_t... i>
197 static int SQCall(Tcls *instance, Tretval(Tcls:: *func)(Targs...), [[maybe_unused]] HSQUIRRELVM vm, std::index_sequence<i...>)
198 {
199 if constexpr (std::is_void_v<Tretval>) {
200 (instance->*func)(
201 Param<Targs>::Get(vm, 2 + i)...
202 );
203 return 0;
204 } else {
205 Tretval ret = (instance->*func)(
206 Param<Targs>::Get(vm, 2 + i)...
207 );
208 return Return<Tretval>::Set(vm, ret);
209 }
210 }
211
212 template <size_t... i>
213 static Tcls *SQConstruct(Tcls *, Tretval(Tcls:: *)(Targs...), [[maybe_unused]] HSQUIRRELVM vm, std::index_sequence<i...>)
214 {
215 Tcls *inst = new Tcls(
216 Param<Targs>::Get(vm, 2 + i)...
217 );
218
219 return inst;
220 }
221 };
222
223
229 template <typename Tcls, typename Tmethod, ScriptType Ttype>
230 inline SQInteger DefSQNonStaticCallback(HSQUIRRELVM vm)
231 {
232 /* Find the amount of params we got */
233 int nparam = sq_gettop(vm);
234 SQUserPointer ptr = nullptr;
235 SQUserPointer real_instance = nullptr;
236 HSQOBJECT instance;
237
238 /* Get the 'SQ' instance of this class */
239 Squirrel::GetInstance(vm, &instance);
240
241 /* Protect against calls to a non-static method in a static way */
242 sq_pushroottable(vm);
243 PushClassName<Tcls, Ttype>(vm);
244 sq_get(vm, -2);
245 sq_pushobject(vm, instance);
246 if (sq_instanceof(vm) != SQTrue) return sq_throwerror(vm, "class method is non-static");
247 sq_pop(vm, 3);
248
249 /* Get the 'real' instance of this class */
250 sq_getinstanceup(vm, 1, &real_instance, nullptr);
251 /* Get the real function pointer */
252 sq_getuserdata(vm, nparam, &ptr, nullptr);
253 if (real_instance == nullptr) return sq_throwerror(vm, "couldn't detect real instance of class for non-static call");
254 /* Remove the userdata from the stack */
255 sq_pop(vm, 1);
256
257 try {
258 /* Delegate it to a template that can handle this specific function */
259 auto cls_instance = static_cast<Tcls *>(real_instance);
260 auto method = *static_cast<Tmethod *>(ptr);
261 return HelperT<Tmethod>::SQCall(cls_instance, method, vm);
262 } catch (SQInteger &e) {
263 return e;
264 }
265 }
266
272 template <typename Tcls, typename Tmethod, ScriptType Ttype>
273 inline SQInteger DefSQAdvancedNonStaticCallback(HSQUIRRELVM vm)
274 {
275 /* Find the amount of params we got */
276 int nparam = sq_gettop(vm);
277 SQUserPointer ptr = nullptr;
278 SQUserPointer real_instance = nullptr;
279 HSQOBJECT instance;
280
281 /* Get the 'SQ' instance of this class */
282 Squirrel::GetInstance(vm, &instance);
283
284 /* Protect against calls to a non-static method in a static way */
285 sq_pushroottable(vm);
286 PushClassName<Tcls, Ttype>(vm);
287 sq_get(vm, -2);
288 sq_pushobject(vm, instance);
289 if (sq_instanceof(vm) != SQTrue) return sq_throwerror(vm, "class method is non-static");
290 sq_pop(vm, 3);
291
292 /* Get the 'real' instance of this class */
293 sq_getinstanceup(vm, 1, &real_instance, nullptr);
294 /* Get the real function pointer */
295 sq_getuserdata(vm, nparam, &ptr, nullptr);
296 if (real_instance == nullptr) return sq_throwerror(vm, "couldn't detect real instance of class for non-static call");
297 /* Remove the userdata from the stack */
298 sq_pop(vm, 1);
299
300 try {
301 /* Call the function, which its only param is always the VM */
302 auto cls_instance = static_cast<Tcls *>(real_instance);
303 auto method = *static_cast<Tmethod *>(ptr);
304 return static_cast<SQInteger>((cls_instance->*method)(vm));
305 } catch (SQInteger &e) {
306 return e;
307 }
308 }
309
315 template <typename Tcls, typename Tmethod>
316 inline SQInteger DefSQStaticCallback(HSQUIRRELVM vm)
317 {
318 /* Find the amount of params we got */
319 int nparam = sq_gettop(vm);
320 SQUserPointer ptr = nullptr;
321
322 /* Get the real function pointer */
323 sq_getuserdata(vm, nparam, &ptr, nullptr);
324
325 try {
326 /* Delegate it to a template that can handle this specific function */
327 auto cls_instance = static_cast<Tcls *>(nullptr);
328 auto method = *static_cast<Tmethod *>(ptr);
329 return HelperT<Tmethod>::SQCall(cls_instance, method, vm);
330 } catch (SQInteger &e) {
331 return e;
332 }
333 }
334
335
341 template <typename Tcls, typename Tmethod>
342 inline SQInteger DefSQAdvancedStaticCallback(HSQUIRRELVM vm)
343 {
344 /* Find the amount of params we got */
345 int nparam = sq_gettop(vm);
346 SQUserPointer ptr = nullptr;
347
348 /* Get the real function pointer */
349 sq_getuserdata(vm, nparam, &ptr, nullptr);
350 /* Remove the userdata from the stack */
351 sq_pop(vm, 1);
352
353 try {
354 /* Call the function, which its only param is always the VM */
355 auto method = *static_cast<Tmethod *>(ptr);
356 return static_cast<SQInteger>((*method)(vm));
357 } catch (SQInteger &e) {
358 return e;
359 }
360 }
361
366 template <typename Tcls>
367 static SQInteger DefSQDestructorCallback(SQUserPointer p, SQInteger)
368 {
369 /* Remove the real instance too */
370 if (p != nullptr) ((Tcls *)p)->Release();
371 return 0;
372 }
373
379 template <typename Tcls, typename Tmethod, int Tnparam>
380 inline SQInteger DefSQConstructorCallback(HSQUIRRELVM vm)
381 {
382 try {
383 /* Create the real instance */
384 Tcls *instance = HelperT<Tmethod>::SQConstruct((Tcls *)nullptr, (Tmethod)nullptr, vm);
385 sq_setinstanceup(vm, -Tnparam, instance);
386 sq_setreleasehook(vm, -Tnparam, DefSQDestructorCallback<Tcls>);
387 instance->AddRef();
388 return 0;
389 } catch (SQInteger &e) {
390 return e;
391 }
392 }
393
398 template <typename Tcls>
399 inline SQInteger DefSQAdvancedConstructorCallback(HSQUIRRELVM vm)
400 {
401 try {
402 /* Find the amount of params we got */
403 int nparam = sq_gettop(vm);
404
405 /* Create the real instance */
406 Tcls *instance = new Tcls(vm);
407 sq_setinstanceup(vm, -nparam, instance);
408 sq_setreleasehook(vm, -nparam, DefSQDestructorCallback<Tcls>);
409 instance->AddRef();
410 return 0;
411 } catch (SQInteger &e) {
412 return e;
413 }
414 }
415
416} // namespace SQConvert
417
418#endif /* SQUIRREL_HELPER_HPP */
static bool GetInstance(HSQUIRRELVM vm, HSQOBJECT *ptr, int pos=1)
Get the Squirrel-instance pointer.
Definition squirrel.hpp:205
constexpr std::underlying_type_t< enum_type > to_underlying(enum_type e)
Implementation of std::to_underlying (from C++23)
Definition enum_type.hpp:17
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:125
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