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/>.
8 /** @file squirrel_helper.hpp declarations and parts of the implementation of the class for convert code */
10 #ifndef SQUIRREL_HELPER_HPP
11 #define SQUIRREL_HELPER_HPP
13 #include "squirrel.hpp"
14 #include "../core/alloc_func.hpp"
15 #include "../economy_type.h"
16 #include "../string_func.h"
17 #include "../tile_type.h"
18 #include "squirrel_helper_type.hpp"
20 template <class CL
, ScriptType ST
> const char *GetClassName();
23 * The Squirrel convert routines
27 * To return a value to squirrel, we use this helper class. It converts to the right format.
28 * We use a class instead of a plain function to allow us to use partial template specializations.
30 template <typename T
> struct Return
;
32 template <> struct Return
<uint8_t> { static inline int Set(HSQUIRRELVM vm
, uint8_t res
) { sq_pushinteger(vm
, (int32_t)res
); return 1; } };
33 template <> struct Return
<uint16_t> { static inline int Set(HSQUIRRELVM vm
, uint16_t res
) { sq_pushinteger(vm
, (int32_t)res
); return 1; } };
34 template <> struct Return
<uint32_t> { static inline int Set(HSQUIRRELVM vm
, uint32_t res
) { sq_pushinteger(vm
, (int32_t)res
); return 1; } };
35 template <> struct Return
<int8_t> { static inline int Set(HSQUIRRELVM vm
, int8_t res
) { sq_pushinteger(vm
, res
); return 1; } };
36 template <> struct Return
<int16_t> { static inline int Set(HSQUIRRELVM vm
, int16_t res
) { sq_pushinteger(vm
, res
); return 1; } };
37 template <> struct Return
<int32_t> { static inline int Set(HSQUIRRELVM vm
, int32_t res
) { sq_pushinteger(vm
, res
); return 1; } };
38 template <> struct Return
<int64_t> { static inline int Set(HSQUIRRELVM vm
, int64_t res
) { sq_pushinteger(vm
, res
); return 1; } };
39 template <> struct Return
<Money
> { static inline int Set(HSQUIRRELVM vm
, Money res
) { sq_pushinteger(vm
, res
); return 1; } };
40 template <> struct Return
<TileIndex
> { static inline int Set(HSQUIRRELVM vm
, TileIndex res
) { sq_pushinteger(vm
, (int32_t)res
.base()); return 1; } };
41 template <> struct Return
<bool> { static inline int Set(HSQUIRRELVM vm
, bool res
) { sq_pushbool (vm
, res
); return 1; } };
42 template <> struct Return
<char *> { /* Do not use char *, use std::optional<std::string> instead. */ };
43 template <> struct Return
<const char *> { /* Do not use const char *, use std::optional<std::string> instead. */ };
44 template <> struct Return
<void *> { static inline int Set(HSQUIRRELVM vm
, void *res
) { sq_pushuserpointer(vm
, res
); return 1; } };
45 template <> struct Return
<HSQOBJECT
> { static inline int Set(HSQUIRRELVM vm
, HSQOBJECT res
) { sq_pushobject(vm
, res
); return 1; } };
47 template <> struct Return
<std::optional
<std::string
>> {
48 static inline int Set(HSQUIRRELVM vm
, std::optional
<std::string
> res
)
50 if (res
.has_value()) {
51 sq_pushstring(vm
, res
.value(), -1);
60 * To get a param from squirrel, we use this helper class. It converts to the right format.
61 * We use a class instead of a plain function to allow us to use partial template specializations.
63 template <typename T
> struct Param
;
65 template <> struct Param
<uint8_t> { static inline uint8_t Get(HSQUIRRELVM vm
, int index
) { SQInteger tmp
; sq_getinteger (vm
, index
, &tmp
); return tmp
; } };
66 template <> struct Param
<uint16_t> { static inline uint16_t Get(HSQUIRRELVM vm
, int index
) { SQInteger tmp
; sq_getinteger (vm
, index
, &tmp
); return tmp
; } };
67 template <> struct Param
<uint32_t> { static inline uint32_t Get(HSQUIRRELVM vm
, int index
) { SQInteger tmp
; sq_getinteger (vm
, index
, &tmp
); return tmp
; } };
68 template <> struct Param
<int8_t> { static inline int8_t Get(HSQUIRRELVM vm
, int index
) { SQInteger tmp
; sq_getinteger (vm
, index
, &tmp
); return tmp
; } };
69 template <> struct Param
<int16_t> { static inline int16_t Get(HSQUIRRELVM vm
, int index
) { SQInteger tmp
; sq_getinteger (vm
, index
, &tmp
); return tmp
; } };
70 template <> struct Param
<int32_t> { static inline int32_t Get(HSQUIRRELVM vm
, int index
) { SQInteger tmp
; sq_getinteger (vm
, index
, &tmp
); return tmp
; } };
71 template <> struct Param
<int64_t> { static inline int64_t Get(HSQUIRRELVM vm
, int index
) { SQInteger tmp
; sq_getinteger (vm
, index
, &tmp
); return tmp
; } };
72 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
); } };
73 template <> struct Param
<Money
> { static inline Money
Get(HSQUIRRELVM vm
, int index
) { SQInteger tmp
; sq_getinteger (vm
, index
, &tmp
); return tmp
; } };
74 template <> struct Param
<bool> { static inline bool Get(HSQUIRRELVM vm
, int index
) { SQBool tmp
; sq_getbool (vm
, index
, &tmp
); return tmp
!= 0; } };
75 template <> struct Param
<const char *> { /* Do not use const char *, use std::string& instead. */ };
76 template <> struct Param
<void *> { static inline void *Get(HSQUIRRELVM vm
, int index
) { SQUserPointer tmp
; sq_getuserpointer(vm
, index
, &tmp
); return tmp
; } };
78 template <> struct Param
<const std::string
&> {
79 static inline const std::string
Get(HSQUIRRELVM vm
, int index
)
81 /* Convert what-ever there is as parameter to a string */
82 sq_tostring(vm
, index
);
85 sq_getstring(vm
, -1, &tmp
);
86 std::string result
= StrMakeValid(tmp
);
92 template <typename Titem
>
93 struct Param
<Array
<Titem
> &&> {
94 static inline Array
<Titem
> Get(HSQUIRRELVM vm
, int index
)
96 /* Sanity check of the size. */
97 if (sq_getsize(vm
, index
) > UINT16_MAX
) throw sq_throwerror(vm
, "an array used as parameter to a function is too large");
100 sq_getstackobj(vm
, index
, &obj
);
101 sq_pushobject(vm
, obj
);
106 while (SQ_SUCCEEDED(sq_next(vm
, -2))) {
107 data
.emplace_back(Param
<Titem
>::Get(vm
, -1));
117 * Helper class to recognize the function type (retval type, args) and use the proper specialization
118 * for SQ callback. The partial specializations for the second arg (Tis_void_retval) are not possible
119 * on the function. Therefore the class is used instead.
121 template <typename Tfunc
> struct HelperT
;
124 * The real C++ caller for functions.
126 template <typename Tretval
, typename
... Targs
>
127 struct HelperT
<Tretval (*)(Targs
...)> {
128 static int SQCall(void *instance
, Tretval(*func
)(Targs
...), HSQUIRRELVM vm
)
130 return SQCall(instance
, func
, vm
, std::index_sequence_for
<Targs
...>{});
134 template <size_t... i
>
135 static int SQCall(void *, Tretval(*func
)(Targs
...), [[maybe_unused
]] HSQUIRRELVM vm
, std::index_sequence
<i
...>)
137 if constexpr (std::is_void_v
<Tretval
>) {
139 Param
<Targs
>::Get(vm
, 2 + i
)...
143 Tretval ret
= (*func
)(
144 Param
<Targs
>::Get(vm
, 2 + i
)...
146 return Return
<Tretval
>::Set(vm
, ret
);
152 * The real C++ caller for methods.
154 template <class Tcls
, typename Tretval
, typename
... Targs
>
155 struct HelperT
<Tretval(Tcls:: *)(Targs
...)> {
156 static int SQCall(Tcls
*instance
, Tretval(Tcls:: *func
)(Targs
...), HSQUIRRELVM vm
)
158 return SQCall(instance
, func
, vm
, std::index_sequence_for
<Targs
...>{});
161 static Tcls
*SQConstruct(Tcls
*instance
, Tretval(Tcls:: *func
)(Targs
...), HSQUIRRELVM vm
)
163 return SQConstruct(instance
, func
, vm
, std::index_sequence_for
<Targs
...>{});
167 template <size_t... i
>
168 static int SQCall(Tcls
*instance
, Tretval(Tcls:: *func
)(Targs
...), [[maybe_unused
]] HSQUIRRELVM vm
, std::index_sequence
<i
...>)
170 if constexpr (std::is_void_v
<Tretval
>) {
172 Param
<Targs
>::Get(vm
, 2 + i
)...
176 Tretval ret
= (instance
->*func
)(
177 Param
<Targs
>::Get(vm
, 2 + i
)...
179 return Return
<Tretval
>::Set(vm
, ret
);
183 template <size_t... i
>
184 static Tcls
*SQConstruct(Tcls
*, Tretval(Tcls:: *)(Targs
...), [[maybe_unused
]] HSQUIRRELVM vm
, std::index_sequence
<i
...>)
186 Tcls
*inst
= new Tcls(
187 Param
<Targs
>::Get(vm
, 2 + i
)...
196 * A general template for all non-static method callbacks from Squirrel.
197 * In here the function_proc is recovered, and the SQCall is called that
198 * can handle this exact amount of params.
200 template <typename Tcls
, typename Tmethod
, ScriptType Ttype
>
201 inline SQInteger
DefSQNonStaticCallback(HSQUIRRELVM vm
)
203 /* Find the amount of params we got */
204 int nparam
= sq_gettop(vm
);
205 SQUserPointer ptr
= nullptr;
206 SQUserPointer real_instance
= nullptr;
209 /* Get the 'SQ' instance of this class */
210 Squirrel::GetInstance(vm
, &instance
);
212 /* Protect against calls to a non-static method in a static way */
213 sq_pushroottable(vm
);
214 const char *className
= GetClassName
<Tcls
, Ttype
>();
215 sq_pushstring(vm
, className
, -1);
217 sq_pushobject(vm
, instance
);
218 if (sq_instanceof(vm
) != SQTrue
) return sq_throwerror(vm
, "class method is non-static");
221 /* Get the 'real' instance of this class */
222 sq_getinstanceup(vm
, 1, &real_instance
, nullptr);
223 /* Get the real function pointer */
224 sq_getuserdata(vm
, nparam
, &ptr
, nullptr);
225 if (real_instance
== nullptr) return sq_throwerror(vm
, "couldn't detect real instance of class for non-static call");
226 /* Remove the userdata from the stack */
230 /* Delegate it to a template that can handle this specific function */
231 return HelperT
<Tmethod
>::SQCall((Tcls
*)real_instance
, *(Tmethod
*)ptr
, vm
);
232 } catch (SQInteger
&e
) {
238 * A general template for all non-static advanced method callbacks from Squirrel.
239 * In here the function_proc is recovered, and the SQCall is called that
240 * can handle this exact amount of params.
242 template <typename Tcls
, typename Tmethod
, ScriptType Ttype
>
243 inline SQInteger
DefSQAdvancedNonStaticCallback(HSQUIRRELVM vm
)
245 /* Find the amount of params we got */
246 int nparam
= sq_gettop(vm
);
247 SQUserPointer ptr
= nullptr;
248 SQUserPointer real_instance
= nullptr;
251 /* Get the 'SQ' instance of this class */
252 Squirrel::GetInstance(vm
, &instance
);
254 /* Protect against calls to a non-static method in a static way */
255 sq_pushroottable(vm
);
256 const char *className
= GetClassName
<Tcls
, Ttype
>();
257 sq_pushstring(vm
, className
, -1);
259 sq_pushobject(vm
, instance
);
260 if (sq_instanceof(vm
) != SQTrue
) return sq_throwerror(vm
, "class method is non-static");
263 /* Get the 'real' instance of this class */
264 sq_getinstanceup(vm
, 1, &real_instance
, nullptr);
265 /* Get the real function pointer */
266 sq_getuserdata(vm
, nparam
, &ptr
, nullptr);
267 if (real_instance
== nullptr) return sq_throwerror(vm
, "couldn't detect real instance of class for non-static call");
268 /* Remove the userdata from the stack */
271 /* Call the function, which its only param is always the VM */
272 return (SQInteger
)(((Tcls
*)real_instance
)->*(*(Tmethod
*)ptr
))(vm
);
276 * A general template for all function/static method callbacks from Squirrel.
277 * In here the function_proc is recovered, and the SQCall is called that
278 * can handle this exact amount of params.
280 template <typename Tcls
, typename Tmethod
>
281 inline SQInteger
DefSQStaticCallback(HSQUIRRELVM vm
)
283 /* Find the amount of params we got */
284 int nparam
= sq_gettop(vm
);
285 SQUserPointer ptr
= nullptr;
287 /* Get the real function pointer */
288 sq_getuserdata(vm
, nparam
, &ptr
, nullptr);
291 /* Delegate it to a template that can handle this specific function */
292 return HelperT
<Tmethod
>::SQCall((Tcls
*)nullptr, *(Tmethod
*)ptr
, vm
);
293 } catch (SQInteger
&e
) {
300 * A general template for all static advanced method callbacks from Squirrel.
301 * In here the function_proc is recovered, and the SQCall is called that
302 * can handle this exact amount of params.
304 template <typename Tcls
, typename Tmethod
>
305 inline SQInteger
DefSQAdvancedStaticCallback(HSQUIRRELVM vm
)
307 /* Find the amount of params we got */
308 int nparam
= sq_gettop(vm
);
309 SQUserPointer ptr
= nullptr;
311 /* Get the real function pointer */
312 sq_getuserdata(vm
, nparam
, &ptr
, nullptr);
313 /* Remove the userdata from the stack */
316 /* Call the function, which its only param is always the VM */
317 return (SQInteger
)(*(*(Tmethod
*)ptr
))(vm
);
321 * A general template for the destructor of SQ instances. This is needed
322 * here as it has to be in the same scope as DefSQConstructorCallback.
324 template <typename Tcls
>
325 static SQInteger
DefSQDestructorCallback(SQUserPointer p
, SQInteger
)
327 /* Remove the real instance too */
328 if (p
!= nullptr) ((Tcls
*)p
)->Release();
333 * A general template to handle creating of instance with any amount of
334 * params. It creates the instance in C++, and it sets all the needed
335 * settings in SQ to register the instance.
337 template <typename Tcls
, typename Tmethod
, int Tnparam
>
338 inline SQInteger
DefSQConstructorCallback(HSQUIRRELVM vm
)
341 /* Create the real instance */
342 Tcls
*instance
= HelperT
<Tmethod
>::SQConstruct((Tcls
*)nullptr, (Tmethod
)nullptr, vm
);
343 sq_setinstanceup(vm
, -Tnparam
, instance
);
344 sq_setreleasehook(vm
, -Tnparam
, DefSQDestructorCallback
<Tcls
>);
347 } catch (SQInteger
&e
) {
353 * A general template to handle creating of an instance with a complex
356 template <typename Tcls
>
357 inline SQInteger
DefSQAdvancedConstructorCallback(HSQUIRRELVM vm
)
360 /* Find the amount of params we got */
361 int nparam
= sq_gettop(vm
);
363 /* Create the real instance */
364 Tcls
*instance
= new Tcls(vm
);
365 sq_setinstanceup(vm
, -nparam
, instance
);
366 sq_setreleasehook(vm
, -nparam
, DefSQDestructorCallback
<Tcls
>);
369 } catch (SQInteger
&e
) {
374 } // namespace SQConvert
376 #endif /* SQUIRREL_HELPER_HPP */