OpenTTD Source  20240919-master-gdf0233f4c2
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 "../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"
19 
20 template <class CL, ScriptType ST> const char *GetClassName();
21 
25 namespace SQConvert {
30  template <typename T> struct Return;
31 
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; } };
46 
47  template <> struct Return<std::optional<std::string>> {
48  static inline int Set(HSQUIRRELVM vm, std::optional<std::string> res)
49  {
50  if (res.has_value()) {
51  sq_pushstring(vm, res.value(), -1);
52  } else {
53  sq_pushnull(vm);
54  }
55  return 1;
56  }
57  };
58 
63  template <typename T> struct Param;
64 
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; } };
77 
78  template <> struct Param<const std::string &> {
79  static inline const std::string Get(HSQUIRRELVM vm, int index)
80  {
81  /* Convert what-ever there is as parameter to a string */
82  sq_tostring(vm, index);
83 
84  const SQChar *tmp;
85  sq_getstring(vm, -1, &tmp);
86  std::string result = StrMakeValid(tmp);
87  sq_poptop(vm);
88  return result;
89  }
90  };
91 
92  template <typename Titem>
93  struct Param<Array<Titem> &&> {
94  static inline Array<Titem> Get(HSQUIRRELVM vm, int index)
95  {
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");
98 
99  SQObject obj;
100  sq_getstackobj(vm, index, &obj);
101  sq_pushobject(vm, obj);
102  sq_pushnull(vm);
103 
104  Array<Titem> data;
105 
106  while (SQ_SUCCEEDED(sq_next(vm, -2))) {
107  data.emplace_back(Param<Titem>::Get(vm, -1));
108  sq_pop(vm, 2);
109  }
110  sq_pop(vm, 2);
111 
112  return data;
113  }
114  };
115 
121  template <typename Tfunc> struct HelperT;
122 
126  template <typename Tretval, typename... Targs>
127  struct HelperT<Tretval (*)(Targs...)> {
128  static int SQCall(void *instance, Tretval(*func)(Targs...), HSQUIRRELVM vm)
129  {
130  return SQCall(instance, func, vm, std::index_sequence_for<Targs...>{});
131  }
132 
133  private:
134  template <size_t... i>
135  static int SQCall(void *, Tretval(*func)(Targs...), [[maybe_unused]] HSQUIRRELVM vm, std::index_sequence<i...>)
136  {
137  if constexpr (std::is_void_v<Tretval>) {
138  (*func)(
139  Param<Targs>::Get(vm, 2 + i)...
140  );
141  return 0;
142  } else {
143  Tretval ret = (*func)(
144  Param<Targs>::Get(vm, 2 + i)...
145  );
146  return Return<Tretval>::Set(vm, ret);
147  }
148  }
149  };
150 
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)
157  {
158  return SQCall(instance, func, vm, std::index_sequence_for<Targs...>{});
159  }
160 
161  static Tcls *SQConstruct(Tcls *instance, Tretval(Tcls:: *func)(Targs...), HSQUIRRELVM vm)
162  {
163  return SQConstruct(instance, func, vm, std::index_sequence_for<Targs...>{});
164  }
165 
166  private:
167  template <size_t... i>
168  static int SQCall(Tcls *instance, Tretval(Tcls:: *func)(Targs...), [[maybe_unused]] HSQUIRRELVM vm, std::index_sequence<i...>)
169  {
170  if constexpr (std::is_void_v<Tretval>) {
171  (instance->*func)(
172  Param<Targs>::Get(vm, 2 + i)...
173  );
174  return 0;
175  } else {
176  Tretval ret = (instance->*func)(
177  Param<Targs>::Get(vm, 2 + i)...
178  );
179  return Return<Tretval>::Set(vm, ret);
180  }
181  }
182 
183  template <size_t... i>
184  static Tcls *SQConstruct(Tcls *, Tretval(Tcls:: *)(Targs...), [[maybe_unused]] HSQUIRRELVM vm, std::index_sequence<i...>)
185  {
186  Tcls *inst = new Tcls(
187  Param<Targs>::Get(vm, 2 + i)...
188  );
189 
190  return inst;
191  }
192  };
193 
194 
200  template <typename Tcls, typename Tmethod, ScriptType Ttype>
201  inline SQInteger DefSQNonStaticCallback(HSQUIRRELVM vm)
202  {
203  /* Find the amount of params we got */
204  int nparam = sq_gettop(vm);
205  SQUserPointer ptr = nullptr;
206  SQUserPointer real_instance = nullptr;
207  HSQOBJECT instance;
208 
209  /* Get the 'SQ' instance of this class */
210  Squirrel::GetInstance(vm, &instance);
211 
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);
216  sq_get(vm, -2);
217  sq_pushobject(vm, instance);
218  if (sq_instanceof(vm) != SQTrue) return sq_throwerror(vm, "class method is non-static");
219  sq_pop(vm, 3);
220 
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 */
227  sq_pop(vm, 1);
228 
229  try {
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) {
233  return e;
234  }
235  }
236 
242  template <typename Tcls, typename Tmethod, ScriptType Ttype>
243  inline SQInteger DefSQAdvancedNonStaticCallback(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);
256  const char *className = GetClassName<Tcls, Ttype>();
257  sq_pushstring(vm, className, -1);
258  sq_get(vm, -2);
259  sq_pushobject(vm, instance);
260  if (sq_instanceof(vm) != SQTrue) return sq_throwerror(vm, "class method is non-static");
261  sq_pop(vm, 3);
262 
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 */
269  sq_pop(vm, 1);
270 
271  /* Call the function, which its only param is always the VM */
272  return (SQInteger)(((Tcls *)real_instance)->*(*(Tmethod *)ptr))(vm);
273  }
274 
280  template <typename Tcls, typename Tmethod>
281  inline SQInteger DefSQStaticCallback(HSQUIRRELVM vm)
282  {
283  /* Find the amount of params we got */
284  int nparam = sq_gettop(vm);
285  SQUserPointer ptr = nullptr;
286 
287  /* Get the real function pointer */
288  sq_getuserdata(vm, nparam, &ptr, nullptr);
289 
290  try {
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) {
294  return e;
295  }
296  }
297 
298 
304  template <typename Tcls, typename Tmethod>
305  inline SQInteger DefSQAdvancedStaticCallback(HSQUIRRELVM vm)
306  {
307  /* Find the amount of params we got */
308  int nparam = sq_gettop(vm);
309  SQUserPointer ptr = nullptr;
310 
311  /* Get the real function pointer */
312  sq_getuserdata(vm, nparam, &ptr, nullptr);
313  /* Remove the userdata from the stack */
314  sq_pop(vm, 1);
315 
316  /* Call the function, which its only param is always the VM */
317  return (SQInteger)(*(*(Tmethod *)ptr))(vm);
318  }
319 
324  template <typename Tcls>
325  static SQInteger DefSQDestructorCallback(SQUserPointer p, SQInteger)
326  {
327  /* Remove the real instance too */
328  if (p != nullptr) ((Tcls *)p)->Release();
329  return 0;
330  }
331 
337  template <typename Tcls, typename Tmethod, int Tnparam>
338  inline SQInteger DefSQConstructorCallback(HSQUIRRELVM vm)
339  {
340  try {
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>);
345  instance->AddRef();
346  return 0;
347  } catch (SQInteger &e) {
348  return e;
349  }
350  }
351 
356  template <typename Tcls>
357  inline SQInteger DefSQAdvancedConstructorCallback(HSQUIRRELVM vm)
358  {
359  try {
360  /* Find the amount of params we got */
361  int nparam = sq_gettop(vm);
362 
363  /* Create the real instance */
364  Tcls *instance = new Tcls(vm);
365  sq_setinstanceup(vm, -nparam, instance);
366  sq_setreleasehook(vm, -nparam, DefSQDestructorCallback<Tcls>);
367  instance->AddRef();
368  return 0;
369  } catch (SQInteger &e) {
370  return e;
371  }
372  }
373 
374 } // namespace SQConvert
375 
376 #endif /* SQUIRREL_HELPER_HPP */
SQConvert::DefSQStaticCallback
SQInteger DefSQStaticCallback(HSQUIRRELVM vm)
A general template for all function/static method callbacks from Squirrel.
Definition: squirrel_helper.hpp:281
StrMakeValid
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
StrongType::Typedef< uint32_t, struct TileIndexTag, StrongType::Compare, StrongType::Integer, StrongType::Compatible< int32_t >, StrongType::Compatible< int64_t > >
Squirrel::GetInstance
static bool GetInstance(HSQUIRRELVM vm, HSQOBJECT *ptr, int pos=1)
Get the Squirrel-instance pointer.
Definition: squirrel.hpp:200
SQConvert::DefSQNonStaticCallback
SQInteger DefSQNonStaticCallback(HSQUIRRELVM vm)
A general template for all non-static method callbacks from Squirrel.
Definition: squirrel_helper.hpp:201
Array
std::vector< Titem > Array
Definition of a simple array.
Definition: squirrel_helper_type.hpp:16
SQConvert::DefSQAdvancedConstructorCallback
SQInteger DefSQAdvancedConstructorCallback(HSQUIRRELVM vm)
A general template to handle creating of an instance with a complex constructor.
Definition: squirrel_helper.hpp:357
SQConvert::Param
To get a param from squirrel, we use this helper class.
Definition: squirrel_helper.hpp:63
SQConvert
The Squirrel convert routines.
Definition: squirrel_helper.hpp:25
squirrel_helper_type.hpp
SQConvert::DefSQAdvancedStaticCallback
SQInteger DefSQAdvancedStaticCallback(HSQUIRRELVM vm)
A general template for all static advanced method callbacks from Squirrel.
Definition: squirrel_helper.hpp:305
SQConvert::DefSQAdvancedNonStaticCallback
SQInteger DefSQAdvancedNonStaticCallback(HSQUIRRELVM vm)
A general template for all non-static advanced method callbacks from Squirrel.
Definition: squirrel_helper.hpp:243
TileIndex
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
SQConvert::HelperT
Helper class to recognize the function type (retval type, args) and use the proper specialization for...
Definition: squirrel_helper.hpp:121
SQConvert::Return
To return a value to squirrel, we use this helper class.
Definition: squirrel_helper.hpp:30
OverflowSafeInt< int64_t >
SQConvert::DefSQDestructorCallback
static SQInteger DefSQDestructorCallback(SQUserPointer p, SQInteger)
A general template for the destructor of SQ instances.
Definition: squirrel_helper.hpp:325
SQConvert::DefSQConstructorCallback
SQInteger DefSQConstructorCallback(HSQUIRRELVM vm)
A general template to handle creating of instance with any amount of params.
Definition: squirrel_helper.hpp:338
squirrel.hpp