OpenTTD Source  13.2.1
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/smallvec_type.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 {
31  struct SQAutoFreePointers : std::vector<void *> {
33  {
34  for (void * p : *this) free(p);
35  }
36  };
37 
38 
42  template <typename T> class ForceType { };
43 
47  template <typename T> static int Return(HSQUIRRELVM vm, T t);
48 
49  template <> inline int Return<uint8> (HSQUIRRELVM vm, uint8 res) { sq_pushinteger(vm, (int32)res); return 1; }
50  template <> inline int Return<uint16> (HSQUIRRELVM vm, uint16 res) { sq_pushinteger(vm, (int32)res); return 1; }
51  template <> inline int Return<uint32> (HSQUIRRELVM vm, uint32 res) { sq_pushinteger(vm, (int32)res); return 1; }
52  template <> inline int Return<int8> (HSQUIRRELVM vm, int8 res) { sq_pushinteger(vm, res); return 1; }
53  template <> inline int Return<int16> (HSQUIRRELVM vm, int16 res) { sq_pushinteger(vm, res); return 1; }
54  template <> inline int Return<int32> (HSQUIRRELVM vm, int32 res) { sq_pushinteger(vm, res); return 1; }
55  template <> inline int Return<int64> (HSQUIRRELVM vm, int64 res) { sq_pushinteger(vm, res); return 1; }
56  template <> inline int Return<Money> (HSQUIRRELVM vm, Money res) { sq_pushinteger(vm, res); return 1; }
57  template <> inline int Return<TileIndex> (HSQUIRRELVM vm, TileIndex res) { sq_pushinteger(vm, (int32)res.value); return 1; }
58  template <> inline int Return<bool> (HSQUIRRELVM vm, bool res) { sq_pushbool (vm, res); return 1; }
59  template <> inline int Return<char *> (HSQUIRRELVM vm, char *res) { if (res == nullptr) sq_pushnull(vm); else { sq_pushstring(vm, res, -1); free(res); } return 1; }
60  template <> inline int Return<const char *>(HSQUIRRELVM vm, const char *res) { if (res == nullptr) sq_pushnull(vm); else { sq_pushstring(vm, res, -1); } return 1; }
61  template <> inline int Return<void *> (HSQUIRRELVM vm, void *res) { sq_pushuserpointer(vm, res); return 1; }
62  template <> inline int Return<HSQOBJECT> (HSQUIRRELVM vm, HSQOBJECT res) { sq_pushobject(vm, res); return 1; }
63 
67  template <typename T> static T GetParam(ForceType<T>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr);
68 
69  template <> inline uint8 GetParam(ForceType<uint8> , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; }
70  template <> inline uint16 GetParam(ForceType<uint16> , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; }
71  template <> inline uint32 GetParam(ForceType<uint32> , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; }
72  template <> inline int8 GetParam(ForceType<int8> , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; }
73  template <> inline int16 GetParam(ForceType<int16> , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; }
74  template <> inline int32 GetParam(ForceType<int32> , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; }
75  template <> inline int64 GetParam(ForceType<int64> , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; }
76  template <> inline TileIndex GetParam(ForceType<TileIndex> , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return TileIndex((uint32)(int32)tmp); }
77  template <> inline Money GetParam(ForceType<Money> , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; }
78  template <> inline bool GetParam(ForceType<bool> , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQBool tmp; sq_getbool (vm, index, &tmp); return tmp != 0; }
79  template <> inline void *GetParam(ForceType<void *> , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer tmp; sq_getuserpointer(vm, index, &tmp); return tmp; }
80  template <> inline const char *GetParam(ForceType<const char *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr)
81  {
82  /* Convert what-ever there is as parameter to a string */
83  sq_tostring(vm, index);
84 
85  const SQChar *tmp;
86  sq_getstring(vm, -1, &tmp);
87  char *tmp_str = stredup(tmp);
88  sq_poptop(vm);
89  ptr->push_back((void *)tmp_str);
90  StrMakeValidInPlace(tmp_str);
91  return tmp_str;
92  }
93 
94  template <> inline Array *GetParam(ForceType<Array *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr)
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  std::vector<int32> data;
105 
106  while (SQ_SUCCEEDED(sq_next(vm, -2))) {
107  SQInteger tmp;
108  if (SQ_SUCCEEDED(sq_getinteger(vm, -1, &tmp))) {
109  data.push_back((int32)tmp);
110  } else {
111  sq_pop(vm, 4);
112  throw sq_throwerror(vm, "a member of an array used as parameter to a function is not numeric");
113  }
114 
115  sq_pop(vm, 2);
116  }
117  sq_pop(vm, 2);
118 
119  Array *arr = (Array*)MallocT<byte>(sizeof(Array) + sizeof(int32) * data.size());
120  arr->size = data.size();
121  memcpy(arr->array, data.data(), sizeof(int32) * data.size());
122 
123  ptr->push_back(arr);
124  return arr;
125  }
126 
132  template <typename Tfunc> struct HelperT;
133 
137  template <typename Tretval, typename... Targs>
138  struct HelperT<Tretval (*)(Targs...)> {
139  static int SQCall(void *instance, Tretval(*func)(Targs...), HSQUIRRELVM vm)
140  {
141  return SQCall(instance, func, vm, std::index_sequence_for<Targs...>{});
142  }
143 
144  private:
145  template <size_t... i>
146  static int SQCall(void *instance, Tretval(*func)(Targs...), [[maybe_unused]] HSQUIRRELVM vm, std::index_sequence<i...>)
147  {
148  [[maybe_unused]] SQAutoFreePointers ptr;
149  if constexpr (std::is_void_v<Tretval>) {
150  (*func)(
151  GetParam(ForceType<Targs>(), vm, 2 + i, &ptr)...
152  );
153  return 0;
154  } else {
155  Tretval ret = (*func)(
156  GetParam(ForceType<Targs>(), vm, 2 + i, &ptr)...
157  );
158  return Return(vm, ret);
159  }
160  }
161  };
162 
166  template <class Tcls, typename Tretval, typename... Targs>
167  struct HelperT<Tretval(Tcls:: *)(Targs...)> {
168  static int SQCall(Tcls *instance, Tretval(Tcls:: *func)(Targs...), HSQUIRRELVM vm)
169  {
170  return SQCall(instance, func, vm, std::index_sequence_for<Targs...>{});
171  }
172 
173  static Tcls *SQConstruct(Tcls *instance, Tretval(Tcls:: *func)(Targs...), HSQUIRRELVM vm)
174  {
175  return SQConstruct(instance, func, vm, std::index_sequence_for<Targs...>{});
176  }
177 
178  private:
179  template <size_t... i>
180  static int SQCall(Tcls *instance, Tretval(Tcls:: *func)(Targs...), [[maybe_unused]] HSQUIRRELVM vm, std::index_sequence<i...>)
181  {
182  [[maybe_unused]] SQAutoFreePointers ptr;
183  if constexpr (std::is_void_v<Tretval>) {
184  (instance->*func)(
185  GetParam(ForceType<Targs>(), vm, 2 + i, &ptr)...
186  );
187  return 0;
188  } else {
189  Tretval ret = (instance->*func)(
190  GetParam(ForceType<Targs>(), vm, 2 + i, &ptr)...
191  );
192  return Return(vm, ret);
193  }
194  }
195 
196  template <size_t... i>
197  static Tcls *SQConstruct(Tcls *, Tretval(Tcls:: *func)(Targs...), [[maybe_unused]] HSQUIRRELVM vm, std::index_sequence<i...>)
198  {
199  [[maybe_unused]] SQAutoFreePointers ptr;
200  Tcls *inst = new Tcls(
201  GetParam(ForceType<Targs>(), vm, 2 + i, &ptr)...
202  );
203 
204  return inst;
205  }
206  };
207 
208 
214  template <typename Tcls, typename Tmethod, ScriptType Ttype>
215  inline SQInteger DefSQNonStaticCallback(HSQUIRRELVM vm)
216  {
217  /* Find the amount of params we got */
218  int nparam = sq_gettop(vm);
219  SQUserPointer ptr = nullptr;
220  SQUserPointer real_instance = nullptr;
221  HSQOBJECT instance;
222 
223  /* Get the 'SQ' instance of this class */
224  Squirrel::GetInstance(vm, &instance);
225 
226  /* Protect against calls to a non-static method in a static way */
227  sq_pushroottable(vm);
228  const char *className = GetClassName<Tcls, Ttype>();
229  sq_pushstring(vm, className, -1);
230  sq_get(vm, -2);
231  sq_pushobject(vm, instance);
232  if (sq_instanceof(vm) != SQTrue) return sq_throwerror(vm, "class method is non-static");
233  sq_pop(vm, 3);
234 
235  /* Get the 'real' instance of this class */
236  sq_getinstanceup(vm, 1, &real_instance, nullptr);
237  /* Get the real function pointer */
238  sq_getuserdata(vm, nparam, &ptr, nullptr);
239  if (real_instance == nullptr) return sq_throwerror(vm, "couldn't detect real instance of class for non-static call");
240  /* Remove the userdata from the stack */
241  sq_pop(vm, 1);
242 
243  try {
244  /* Delegate it to a template that can handle this specific function */
245  return HelperT<Tmethod>::SQCall((Tcls *)real_instance, *(Tmethod *)ptr, vm);
246  } catch (SQInteger &e) {
247  return e;
248  }
249  }
250 
256  template <typename Tcls, typename Tmethod, ScriptType Ttype>
257  inline SQInteger DefSQAdvancedNonStaticCallback(HSQUIRRELVM vm)
258  {
259  /* Find the amount of params we got */
260  int nparam = sq_gettop(vm);
261  SQUserPointer ptr = nullptr;
262  SQUserPointer real_instance = nullptr;
263  HSQOBJECT instance;
264 
265  /* Get the 'SQ' instance of this class */
266  Squirrel::GetInstance(vm, &instance);
267 
268  /* Protect against calls to a non-static method in a static way */
269  sq_pushroottable(vm);
270  const char *className = GetClassName<Tcls, Ttype>();
271  sq_pushstring(vm, className, -1);
272  sq_get(vm, -2);
273  sq_pushobject(vm, instance);
274  if (sq_instanceof(vm) != SQTrue) return sq_throwerror(vm, "class method is non-static");
275  sq_pop(vm, 3);
276 
277  /* Get the 'real' instance of this class */
278  sq_getinstanceup(vm, 1, &real_instance, nullptr);
279  /* Get the real function pointer */
280  sq_getuserdata(vm, nparam, &ptr, nullptr);
281  if (real_instance == nullptr) return sq_throwerror(vm, "couldn't detect real instance of class for non-static call");
282  /* Remove the userdata from the stack */
283  sq_pop(vm, 1);
284 
285  /* Call the function, which its only param is always the VM */
286  return (SQInteger)(((Tcls *)real_instance)->*(*(Tmethod *)ptr))(vm);
287  }
288 
294  template <typename Tcls, typename Tmethod>
295  inline SQInteger DefSQStaticCallback(HSQUIRRELVM vm)
296  {
297  /* Find the amount of params we got */
298  int nparam = sq_gettop(vm);
299  SQUserPointer ptr = nullptr;
300 
301  /* Get the real function pointer */
302  sq_getuserdata(vm, nparam, &ptr, nullptr);
303 
304  try {
305  /* Delegate it to a template that can handle this specific function */
306  return HelperT<Tmethod>::SQCall((Tcls *)nullptr, *(Tmethod *)ptr, vm);
307  } catch (SQInteger &e) {
308  return e;
309  }
310  }
311 
312 
318  template <typename Tcls, typename Tmethod>
319  inline SQInteger DefSQAdvancedStaticCallback(HSQUIRRELVM vm)
320  {
321  /* Find the amount of params we got */
322  int nparam = sq_gettop(vm);
323  SQUserPointer ptr = nullptr;
324 
325  /* Get the real function pointer */
326  sq_getuserdata(vm, nparam, &ptr, nullptr);
327  /* Remove the userdata from the stack */
328  sq_pop(vm, 1);
329 
330  /* Call the function, which its only param is always the VM */
331  return (SQInteger)(*(*(Tmethod *)ptr))(vm);
332  }
333 
338  template <typename Tcls>
339  static SQInteger DefSQDestructorCallback(SQUserPointer p, SQInteger size)
340  {
341  /* Remove the real instance too */
342  if (p != nullptr) ((Tcls *)p)->Release();
343  return 0;
344  }
345 
351  template <typename Tcls, typename Tmethod, int Tnparam>
352  inline SQInteger DefSQConstructorCallback(HSQUIRRELVM vm)
353  {
354  try {
355  /* Create the real instance */
356  Tcls *instance = HelperT<Tmethod>::SQConstruct((Tcls *)nullptr, (Tmethod)nullptr, vm);
357  sq_setinstanceup(vm, -Tnparam, instance);
358  sq_setreleasehook(vm, -Tnparam, DefSQDestructorCallback<Tcls>);
359  instance->AddRef();
360  return 0;
361  } catch (SQInteger &e) {
362  return e;
363  }
364  }
365 
370  template <typename Tcls>
371  inline SQInteger DefSQAdvancedConstructorCallback(HSQUIRRELVM vm)
372  {
373  try {
374  /* Find the amount of params we got */
375  int nparam = sq_gettop(vm);
376 
377  /* Create the real instance */
378  Tcls *instance = new Tcls(vm);
379  sq_setinstanceup(vm, -nparam, instance);
380  sq_setreleasehook(vm, -nparam, DefSQDestructorCallback<Tcls>);
381  instance->AddRef();
382  return 0;
383  } catch (SQInteger &e) {
384  return e;
385  }
386  }
387 
388 } // namespace SQConvert
389 
390 #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:295
SQConvert::SQAutoFreePointers
Pointers assigned to this class will be free'd when this instance comes out of scope.
Definition: squirrel_helper.hpp:31
TileIndex
The index/ID of a Tile.
Definition: tile_type.h:85
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:215
Array
Definition of a simple array.
Definition: squirrel_helper_type.hpp:14
StrMakeValidInPlace
void StrMakeValidInPlace(char *str, const char *last, StringValidationSettings settings)
Scans the string for invalid characters and replaces then with a question mark '?' (if not ignored).
Definition: string.cpp:273
SQConvert::DefSQAdvancedConstructorCallback
SQInteger DefSQAdvancedConstructorCallback(HSQUIRRELVM vm)
A general template to handle creating of an instance with a complex constructor.
Definition: squirrel_helper.hpp:371
StrongTypedef::value
T value
Backing storage field.
Definition: strong_typedef_type.hpp:30
SQConvert::ForceType
Special class to make it possible for the compiler to pick the correct GetParam().
Definition: squirrel_helper.hpp:42
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:319
Array::array
int32 array[]
The data of the array.
Definition: squirrel_helper_type.hpp:16
SQConvert::DefSQAdvancedNonStaticCallback
SQInteger DefSQAdvancedNonStaticCallback(HSQUIRRELVM vm)
A general template for all non-static advanced method callbacks from Squirrel.
Definition: squirrel_helper.hpp:257
Array::size
size_t size
The size of the array.
Definition: squirrel_helper_type.hpp:15
stredup
char * stredup(const char *s, const char *last)
Create a duplicate of the given string.
Definition: string.cpp:138
SQConvert::HelperT
Helper class to recognize the function type (retval type, args) and use the proper specialization for...
Definition: squirrel_helper.hpp:132
OverflowSafeInt< int64 >
SQConvert::Return
static int Return(HSQUIRRELVM vm, T t)
To return a value to squirrel, we call this function.
SQConvert::DefSQDestructorCallback
static SQInteger DefSQDestructorCallback(SQUserPointer p, SQInteger size)
A general template for the destructor of SQ instances.
Definition: squirrel_helper.hpp:339
SQConvert::DefSQConstructorCallback
SQInteger DefSQConstructorCallback(HSQUIRRELVM vm)
A general template to handle creating of instance with any amount of params.
Definition: squirrel_helper.hpp:352
squirrel.hpp
free
static void free(const void *ptr)
Version of the standard free that accepts const pointers.
Definition: stdafx.h:470
SQConvert::GetParam
static T GetParam(ForceType< T >, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr)
To get a param from squirrel, we call this function.