OpenTTD Source 20260304-master-g1baaa74679
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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
9
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"
18
24template <class CL, ScriptType ST> SQInteger PushClassName(HSQUIRRELVM vm);
25
29namespace SQConvert {
34 template <typename T> struct Return;
35
36 template <> struct Return<uint8_t> { static inline int Set(HSQUIRRELVM vm, uint8_t res) { sq_pushinteger(vm, (int32_t)res); return 1; } };
37 template <> struct Return<uint16_t> { static inline int Set(HSQUIRRELVM vm, uint16_t res) { sq_pushinteger(vm, (int32_t)res); return 1; } };
38 template <> struct Return<uint32_t> { static inline int Set(HSQUIRRELVM vm, uint32_t res) { sq_pushinteger(vm, (int32_t)res); return 1; } };
39 template <> struct Return<int8_t> { static inline int Set(HSQUIRRELVM vm, int8_t res) { sq_pushinteger(vm, res); return 1; } };
40 template <> struct Return<int16_t> { static inline int Set(HSQUIRRELVM vm, int16_t res) { sq_pushinteger(vm, res); return 1; } };
41 template <> struct Return<int32_t> { static inline int Set(HSQUIRRELVM vm, int32_t res) { sq_pushinteger(vm, res); return 1; } };
42 template <> struct Return<int64_t> { static inline int Set(HSQUIRRELVM vm, int64_t res) { sq_pushinteger(vm, res); return 1; } };
43 template <> struct Return<TileIndex> { static inline int Set(HSQUIRRELVM vm, TileIndex res) { sq_pushinteger(vm, (int32_t)res.base()); return 1; } };
44 template <> struct Return<bool> { static inline int Set(HSQUIRRELVM vm, bool res) { sq_pushbool (vm, res); return 1; } };
45 template <> struct Return<char *> { /* Do not use char *, use std::optional<std::string> instead. */ };
46 template <> struct Return<const char *> { /* Do not use const char *, use std::optional<std::string> instead. */ };
47 template <> struct Return<HSQOBJECT> { static inline int Set(HSQUIRRELVM vm, HSQOBJECT res) { sq_pushobject(vm, res); return 1; } };
48
49 template <typename T> requires std::is_enum_v<T> struct Return<T> {
50 static inline int Set(HSQUIRRELVM vm, T res)
51 {
52 sq_pushinteger(vm, to_underlying(res));
53 return 1;
54 }
55 };
56
57 template <ConvertibleThroughBase T> struct Return<T> {
58 static inline int Set(HSQUIRRELVM vm, T res)
59 {
60 sq_pushinteger(vm, res.base());
61 return 1;
62 }
63 };
64
65 template <> struct Return<std::optional<std::string>> {
66 static inline int Set(HSQUIRRELVM vm, std::optional<std::string> res)
67 {
68 if (res.has_value()) {
69 sq_pushstring(vm, res.value());
70 } else {
71 sq_pushnull(vm);
72 }
73 return 1;
74 }
75 };
76
81 template <typename T> struct Param;
82
83 template <> struct Param<uint8_t> { static inline uint8_t Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } };
84 template <> struct Param<uint16_t> { static inline uint16_t Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } };
85 template <> struct Param<uint32_t> { static inline uint32_t Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } };
86 template <> struct Param<int8_t> { static inline int8_t Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } };
87 template <> struct Param<int16_t> { static inline int16_t Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } };
88 template <> struct Param<int32_t> { static inline int32_t Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } };
89 template <> struct Param<int64_t> { static inline int64_t Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } };
90 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); } };
91 template <> struct Param<bool> { static inline bool Get(HSQUIRRELVM vm, int index) { SQBool tmp; sq_getbool (vm, index, &tmp); return tmp != 0; } };
92 template <> struct Param<const char *> { /* Do not use const char *, use std::string& instead. */ };
93
94 template <typename T> requires std::is_enum_v<T> struct Param<T> {
95 static inline T Get(HSQUIRRELVM vm, int index)
96 {
97 SQInteger tmp;
98 sq_getinteger(vm, index, &tmp);
99 return static_cast<T>(tmp);
100 }
101 };
102
103 template <ConvertibleThroughBase T> struct Param<T> {
104 static inline T Get(HSQUIRRELVM vm, int index)
105 {
106 SQInteger tmp;
107 sq_getinteger(vm, index, &tmp);
108 return T{static_cast<T::BaseType>(tmp)};
109 }
110 };
111
112 template <> struct Param<const std::string &> {
113 static inline const std::string Get(HSQUIRRELVM vm, int index)
114 {
115 /* Convert what-ever there is as parameter to a string */
116 sq_tostring(vm, index);
117
118 std::string_view view;
119 sq_getstring(vm, -1, view);
120 std::string result = StrMakeValid(view);
121 sq_poptop(vm);
122 return result;
123 }
124 };
125
126 template <typename Titem>
127 struct Param<Array<Titem> &&> {
128 static inline Array<Titem> Get(HSQUIRRELVM vm, int index)
129 {
130 /* Sanity check of the size. */
131 if (sq_getsize(vm, index) > UINT16_MAX) throw sq_throwerror(vm, "an array used as parameter to a function is too large");
132
133 SQObject obj;
134 sq_getstackobj(vm, index, &obj);
135 sq_pushobject(vm, obj);
136 sq_pushnull(vm);
137
138 Array<Titem> data;
139
140 while (SQ_SUCCEEDED(sq_next(vm, -2))) {
141 data.emplace_back(Param<Titem>::Get(vm, -1));
142 sq_pop(vm, 2);
143 }
144 sq_pop(vm, 2);
145
146 return data;
147 }
148 };
149
155 template <typename Tfunc> struct HelperT;
156
160 template <typename Tretval, typename... Targs>
161 struct HelperT<Tretval (*)(Targs...)> {
162 static int SQCall(void *instance, Tretval(*func)(Targs...), HSQUIRRELVM vm)
163 {
164 return SQCall(instance, func, vm, std::index_sequence_for<Targs...>{});
165 }
166
167 private:
168 template <size_t... i>
169 static int SQCall(void *, Tretval(*func)(Targs...), [[maybe_unused]] HSQUIRRELVM vm, std::index_sequence<i...>)
170 {
171 if constexpr (std::is_void_v<Tretval>) {
172 (*func)(
173 Param<Targs>::Get(vm, 2 + i)...
174 );
175 return 0;
176 } else {
177 Tretval ret = (*func)(
178 Param<Targs>::Get(vm, 2 + i)...
179 );
180 return Return<Tretval>::Set(vm, std::move(ret));
181 }
182 }
183 };
184
188 template <class Tcls, typename Tretval, typename... Targs>
189 struct HelperT<Tretval(Tcls:: *)(Targs...)> {
190 static int SQCall(Tcls *instance, auto func, HSQUIRRELVM vm)
191 {
192 return SQCall(instance, func, vm, std::index_sequence_for<Targs...>{});
193 }
194
195 static Tcls *SQConstruct(HSQUIRRELVM vm)
196 {
197 return SQConstruct(vm, std::index_sequence_for<Targs...>{});
198 }
199
200 private:
201 template <size_t... i>
202 static int SQCall(Tcls *instance, auto func, [[maybe_unused]] HSQUIRRELVM vm, std::index_sequence<i...>)
203 {
204 if constexpr (std::is_void_v<Tretval>) {
205 (instance->*func)(
206 Param<Targs>::Get(vm, 2 + i)...
207 );
208 return 0;
209 } else {
210 Tretval ret = (instance->*func)(
211 Param<Targs>::Get(vm, 2 + i)...
212 );
213 return Return<Tretval>::Set(vm, std::move(ret));
214 }
215 }
216
217 template <size_t... i>
218 static Tcls *SQConstruct([[maybe_unused]] HSQUIRRELVM vm, std::index_sequence<i...>)
219 {
220 Tcls *inst = new Tcls(
221 Param<Targs>::Get(vm, 2 + i)...
222 );
223
224 return inst;
225 }
226 };
227
231 template <class Tcls, typename Tretval, typename... Targs>
232 struct HelperT<Tretval(Tcls:: *)(Targs...) const> : HelperT<Tretval(Tcls:: *)(Targs...)> {};
233
234
242 template <typename Tcls, typename Tmethod, ScriptType Ttype>
243 inline SQInteger DefSQNonStaticCallback(HSQUIRRELVM vm)
244 {
245 /* Find the amount of params we got */
246 int nparam = sq_gettop(vm);
247 SQUserPointer ptr = nullptr;
248 SQUserPointer real_instance = nullptr;
249 HSQOBJECT instance;
250
251 /* Get the 'SQ' instance of this class */
252 Squirrel::GetInstance(vm, &instance);
253
254 /* Protect against calls to a non-static method in a static way */
255 sq_pushroottable(vm);
257 sq_get(vm, -2);
258 sq_pushobject(vm, instance);
259 if (sq_instanceof(vm) != SQTrue) return sq_throwerror(vm, "class method is non-static");
260 sq_pop(vm, 3);
261
262 /* Get the 'real' instance of this class */
263 sq_getinstanceup(vm, 1, &real_instance, nullptr);
264 /* Get the real function pointer */
265 sq_getuserdata(vm, nparam, &ptr, nullptr);
266 if (real_instance == nullptr) return sq_throwerror(vm, "couldn't detect real instance of class for non-static call");
267 /* Remove the userdata from the stack */
268 sq_pop(vm, 1);
269
270 try {
271 /* Delegate it to a template that can handle this specific function */
272 auto cls_instance = static_cast<Tcls *>(real_instance);
273 auto method = *static_cast<Tmethod *>(ptr);
274 return HelperT<Tmethod>::SQCall(cls_instance, method, vm);
275 } catch (SQInteger &e) {
276 return e;
277 }
278 }
279
287 template <typename Tcls, typename Tmethod, ScriptType Ttype>
288 inline SQInteger DefSQAdvancedNonStaticCallback(HSQUIRRELVM vm)
289 {
290 /* Find the amount of params we got */
291 int nparam = sq_gettop(vm);
292 SQUserPointer ptr = nullptr;
293 SQUserPointer real_instance = nullptr;
294 HSQOBJECT instance;
295
296 /* Get the 'SQ' instance of this class */
297 Squirrel::GetInstance(vm, &instance);
298
299 /* Protect against calls to a non-static method in a static way */
300 sq_pushroottable(vm);
302 sq_get(vm, -2);
303 sq_pushobject(vm, instance);
304 if (sq_instanceof(vm) != SQTrue) return sq_throwerror(vm, "class method is non-static");
305 sq_pop(vm, 3);
306
307 /* Get the 'real' instance of this class */
308 sq_getinstanceup(vm, 1, &real_instance, nullptr);
309 /* Get the real function pointer */
310 sq_getuserdata(vm, nparam, &ptr, nullptr);
311 if (real_instance == nullptr) return sq_throwerror(vm, "couldn't detect real instance of class for non-static call");
312 /* Remove the userdata from the stack */
313 sq_pop(vm, 1);
314
315 try {
316 /* Call the function, which its only param is always the VM */
317 auto cls_instance = static_cast<Tcls *>(real_instance);
318 auto method = *static_cast<Tmethod *>(ptr);
319 return static_cast<SQInteger>((cls_instance->*method)(vm));
320 } catch (SQInteger &e) {
321 return e;
322 }
323 }
324
332 template <typename Tcls, typename Tmethod>
333 inline SQInteger DefSQStaticCallback(HSQUIRRELVM vm)
334 {
335 /* Find the amount of params we got */
336 int nparam = sq_gettop(vm);
337 SQUserPointer ptr = nullptr;
338
339 /* Get the real function pointer */
340 sq_getuserdata(vm, nparam, &ptr, nullptr);
341
342 try {
343 /* Delegate it to a template that can handle this specific function */
344 auto cls_instance = static_cast<Tcls *>(nullptr);
345 auto method = *static_cast<Tmethod *>(ptr);
346 return HelperT<Tmethod>::SQCall(cls_instance, method, vm);
347 } catch (SQInteger &e) {
348 return e;
349 }
350 }
351
352
360 template <typename Tcls, typename Tmethod>
361 inline SQInteger DefSQAdvancedStaticCallback(HSQUIRRELVM vm)
362 {
363 /* Find the amount of params we got */
364 int nparam = sq_gettop(vm);
365 SQUserPointer ptr = nullptr;
366
367 /* Get the real function pointer */
368 sq_getuserdata(vm, nparam, &ptr, nullptr);
369 /* Remove the userdata from the stack */
370 sq_pop(vm, 1);
371
372 try {
373 /* Call the function, which its only param is always the VM */
374 auto method = *static_cast<Tmethod *>(ptr);
375 return static_cast<SQInteger>((*method)(vm));
376 } catch (SQInteger &e) {
377 return e;
378 }
379 }
380
387 template <typename Tcls>
388 static SQInteger DefSQDestructorCallback(SQUserPointer p, SQInteger)
389 {
390 /* Remove the real instance too */
391 if (p != nullptr) ((Tcls *)p)->Release();
392 return 0;
393 }
394
402 template <typename Tcls, typename Tmethod>
403 inline SQInteger DefSQConstructorCallback(HSQUIRRELVM vm)
404 {
405 try {
406 /* Find the amount of params we got */
407 int nparam = sq_gettop(vm);
408
409 /* Create the real instance */
410 Tcls *instance = HelperT<Tmethod>::SQConstruct(vm);
411 sq_setinstanceup(vm, -nparam, instance);
412 sq_setreleasehook(vm, -nparam, DefSQDestructorCallback<Tcls>);
413 instance->AddRef();
414 return 0;
415 } catch (SQInteger &e) {
416 return e;
417 }
418 }
419
426 template <typename Tcls>
427 inline SQInteger DefSQAdvancedConstructorCallback(HSQUIRRELVM vm)
428 {
429 try {
430 /* Find the amount of params we got */
431 int nparam = sq_gettop(vm);
432
433 /* Create the real instance */
434 Tcls *instance = new Tcls(vm);
435 sq_setinstanceup(vm, -nparam, instance);
436 sq_setreleasehook(vm, -nparam, DefSQDestructorCallback<Tcls>);
437 instance->AddRef();
438 return 0;
439 } catch (SQInteger &e) {
440 return e;
441 }
442 }
443
444} // namespace SQConvert
445
446#endif /* SQUIRREL_HELPER_HPP */
static void GetInstance(HSQUIRRELVM vm, HSQOBJECT *ptr, int pos=1)
Get the Squirrel-instance pointer.
Definition squirrel.hpp:308
Concept for unifying the convert through 'base()' behaviour of several 'strong' types.
#define T
Climate temperate.
Definition engines.h:91
constexpr std::underlying_type_t< enum_type > to_underlying(enum_type e)
Implementation of std::to_underlying (from C++23).
Definition enum_type.hpp:21
The Squirrel convert routines.
SQInteger DefSQAdvancedConstructorCallback(HSQUIRRELVM vm)
A general template to handle creating of an instance with a complex constructor.
SQInteger DefSQConstructorCallback(HSQUIRRELVM vm)
A general template to handle creating of instance with any amount of params.
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 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.
SQInteger PushClassName(HSQUIRRELVM vm)
Helper to push the class name of a script type onto the Squirrel stack.
Helper structs for converting Squirrel data structures to C++.
std::vector< Titem, ScriptStdAllocator< Titem > > Array
Definition of a simple array.
static void StrMakeValid(Builder &builder, StringConsumer &consumer, StringValidationSettings settings)
Copies the valid (UTF-8) characters from consumer to the builder.
Definition string.cpp:119
Functions related to low-level strings.
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.
Types related to tiles.
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:92