The Battle for Wesnoth  1.19.4+dev
push_check.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2017 - 2024
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #pragma once
16 
17 #include "scripting/lua_common.hpp"
18 #include "scripting/lua_widget.hpp"
19 
20 #include "lua/wrapper_lauxlib.h"
21 #include "tstring.hpp"
22 #include "map/location.hpp"
23 #include "variable.hpp"
24 
25 #include <cassert>
26 #include <string_view>
27 #include <type_traits>
28 
29 struct lua_index_raw {
30  int index;
31  lua_index_raw(int i) : index(i) {}
32  lua_index_raw(lua_State* L) : index(lua_gettop(L)) {}
33 };
34 
35 namespace lua_check_impl
36 {
37  template<typename T, typename T2 = void>
38  struct is_container : std::false_type {};
39 
40  template<typename T>
41  struct is_container<T, std::void_t<
42  typename std::decay_t<T>::value_type,
43  typename std::decay_t<T>::iterator,
44  typename std::decay_t<T>::size_type,
45  typename std::decay_t<T>::reference>
46  > : std::true_type {};
47 
48  template<class T, template<class> class U>
49  inline constexpr bool is_instance_of_v = std::false_type{};
50 
51  template<template<class> class U, class V>
52  inline constexpr bool is_instance_of_v<U<V>,U> = std::true_type{};
53 
54  template<typename T, typename T2 = void>
55  struct is_map : std::false_type {};
56 
57  template<typename T>
58  struct is_map<T, std::void_t<
59  typename std::decay_t<T>::key_type,
60  typename std::decay_t<T>::mapped_type>
61  > : std::true_type {};
62 
63  template<typename T, typename T2 = void>
64  struct is_pair : std::false_type {};
65 
66  template<typename T>
67  struct is_pair<T, std::void_t<
68  typename std::decay_t<T>::first_type,
69  typename std::decay_t<T>::second_type>
70  > : std::true_type {};
71 
72  template<typename T>
73  std::enable_if_t<std::is_same_v<T, lua_index_raw>, lua_index_raw>
74  lua_check(lua_State * L, int n)
75  {
76  return lua_index_raw{ lua_absindex(L, n) };
77  }
78  template<typename T>
79  std::enable_if_t<std::is_same_v<T, lua_index_raw>, lua_index_raw>
80  lua_to_or_default(lua_State * L, int n, const T& /*def*/)
81  {
82  return lua_index_raw{ lua_absindex(L, n) };
83  }
84  template<typename T>
85  std::enable_if_t<std::is_same_v<T, lua_index_raw>, void>
86  lua_push(lua_State * L, lua_index_raw n)
87  {
88  lua_pushvalue(L, n.index);
89  }
90 
91  //std::string
92  template<typename T>
93  std::enable_if_t<std::is_same_v<T, std::string>, std::string>
94  lua_check(lua_State *L, int n)
95  {
96  return luaL_checkstring(L, n);
97  }
98  template<typename T>
99  std::enable_if_t<std::is_same_v<T, std::string>, std::string>
100  lua_to_or_default(lua_State *L, int n, const T& def)
101  {
102  return luaL_optstring(L, n, def.c_str());
103  }
104  template<typename T>
105  std::enable_if_t<std::is_same_v<T, std::string>, void>
106  lua_push(lua_State *L, const T& val)
107  {
108  lua_pushlstring(L, val.c_str(), val.size());
109  }
110 
111  //std::string_view
112  template<typename T>
113  std::enable_if_t<std::is_same_v<T, std::string_view>, std::string_view>
114  lua_check(lua_State *L, int n)
115  {
116  return luaW_tostring(L, n);
117  }
118  template<typename T>
119  std::enable_if_t<std::is_same_v<T, std::string_view>, std::string_view>
120  lua_to_or_default(lua_State *L, int n, const T& def)
121  {
122  return luaW_tostring_or_default(L, n, def);
123  }
124  template<typename T>
125  std::enable_if_t<std::is_same_v<T, std::string_view>, void>
126  lua_push(lua_State *L, const T& val)
127  {
128  lua_pushlstring(L, val.data(), val.size());
129  }
130 
131  //config
132  template<typename T>
133  std::enable_if_t<std::is_same_v<T, config>, config>
134  lua_check(lua_State *L, int n)
135  {
136  return luaW_checkconfig(L, n);
137  }
138  template<typename T>
139  std::enable_if_t<std::is_same_v<T, config>, config>
140  lua_to_or_default(lua_State *L, int n, const T& def)
141  {
142  config cfg;
143  return luaW_toconfig(L, n, cfg) ? cfg : def;
144  }
145  template<typename T>
146  std::enable_if_t<std::is_same_v<T, config>, void>
147  lua_push(lua_State *L, const config& val)
148  {
149  luaW_pushconfig(L, val);
150  }
151 
152  //vconfig
153  template<typename T>
154  std::enable_if_t<std::is_same_v<T, vconfig>, vconfig>
155  lua_check(lua_State *L, int n)
156  {
157  return luaW_checkvconfig(L, n);
158  }
159  template<typename T>
160  std::enable_if_t<std::is_same_v<T, vconfig>, vconfig>
161  lua_to_or_default(lua_State *L, int n, const T& def)
162  {
164  return luaW_tovconfig(L, n, cfg) ? cfg : def;
165  }
166  template<typename T>
167  std::enable_if_t<std::is_same_v<T, vconfig>, void>
168  lua_push(lua_State *L, const vconfig& val)
169  {
170  luaW_pushvconfig(L, val);
171  }
172 
173  //location
174  template<typename T>
175  std::enable_if_t<std::is_same_v<T, map_location>, map_location>
176  lua_check(lua_State *L, int n)
177  {
178  return luaW_checklocation(L, n);
179  }
180  template<typename T>
181  std::enable_if_t<std::is_same_v<T, map_location>, map_location>
182  lua_to_or_default(lua_State *L, int n, const T& def)
183  {
184  map_location res;
185  if (!luaW_tolocation(L, n, res)) {
186  return def;
187  }
188  return res;
189  }
190  template<typename T>
191  std::enable_if_t<std::is_same_v<T, map_location>, void>
192  lua_push(lua_State *L, const map_location& val)
193  {
194  luaW_pushlocation(L, val);
195  }
196 
197  //t_string
198  template<typename T>
199  std::enable_if_t<std::is_same_v<T, t_string>, t_string>
200  lua_check(lua_State *L, int n)
201  {
202  return luaW_checktstring(L, n);
203  }
204  template<typename T>
205  std::enable_if_t<std::is_same_v<T, t_string>, void>
206  lua_push(lua_State *L, const t_string& val)
207  {
208  luaW_pushtstring(L, val);
209  }
210 
211  //widget
212  //lua_check for widget is not supported because lua_check returns by value
213  template<typename T>
214  std::enable_if_t<std::is_same_v<T, gui2::widget>, void>
215  lua_push(lua_State *L, gui2::widget& val)
216  {
217  luaW_pushwidget(L, val);
218  }
219 
220  //bool
221  template<typename T>
222  std::enable_if_t<std::is_same_v<T, bool>, bool>
223  lua_check(lua_State *L, int n)
224  {
225  return luaW_toboolean(L, n);
226  }
227  template<typename T>
228  std::enable_if_t<std::is_same_v<T, bool>, bool>
229  lua_to_or_default(lua_State *L, int n, const T& /*def*/)
230  {
231  return luaW_toboolean(L, n);
232  }
233  template<typename T>
234  std::enable_if_t<std::is_same_v<T, bool>, void>
235  lua_push(lua_State *L, bool val)
236  {
237  lua_pushboolean(L, val);
238  }
239 
240  //double, float
241  template<typename T>
242  std::enable_if_t<std::is_floating_point_v<T>, T>
243  lua_check(lua_State *L, int n)
244  {
245  return luaL_checknumber(L, n);
246  }
247  template<typename T>
248  std::enable_if_t<std::is_floating_point_v<T>, T>
249  lua_to_or_default(lua_State *L, int n, const T& def)
250  {
251  int isnum;
252  lua_Number d = lua_tonumberx(L, n, &isnum);
253  if (!isnum) {
254  return def;
255  }
256  return d;
257  }
258  template<typename T>
259  std::enable_if_t<std::is_floating_point_v<T>, void>
260  lua_push(lua_State *L, T val)
261  {
262  lua_pushnumber(L, val);
263  }
264 
265  //integer types
266  template<typename T>
267  std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, bool>, T>
268  lua_check(lua_State *L, int n)
269  {
270  return luaL_checkinteger(L, n);
271  }
272  template<typename T>
273  std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, bool>, T>
274  lua_to_or_default(lua_State *L, int n, const T& def)
275  {
276  int isnum;
277  lua_Integer res = lua_tointegerx(L, n, &isnum);
278  if (!isnum) {
279  return def;
280  }
281  return res;
282  }
283 
284  template<typename T>
285  std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, bool>, void>
286  lua_push(lua_State *L, T val)
287  {
288  lua_pushinteger(L, val);
289  }
290 
291  //std::pair
292  //Not sure if the not_<is_const> is required; only (maybe) if std::map matches is_container
293  template<typename T>
294  std::enable_if_t<is_pair<T>::value && !std::is_const_v<typename T::first_type>, T>
295  lua_check(lua_State *L, int n)
296  {
297  T result;
298  if (lua_istable(L, n)) {
299  lua_rawgeti(L, n, 1);
300  result.first = lua_check<const typename T::first_type&>(L, -1);
301  lua_rawgeti(L, n, 2);
302  result.second = lua_check<const typename T::second_type&>(L, -1);
303  lua_pop(L, 2);
304  }
305  return result;
306  }
307  template<typename T>
308  std::enable_if_t<is_pair<T>::value && !std::is_const_v<typename T::first_type>, void>
309  lua_push(lua_State *L, const T& val)
310  {
311  lua_newtable(L);
312  lua_push<const typename T::first_type&>(L, val.first);
313  lua_rawseti(L, -2, 1);
314  lua_push<const typename T::second_type&>(L, val.second);
315  lua_rawseti(L, -2, 2);
316  }
317 
318  //std::vector and similar but not std::string
319  template<typename T>
320  std::enable_if_t<is_container<T>::value && !std::is_same_v<T, std::string> && !std::is_same_v<T, std::string_view>, T>
321  lua_check(lua_State * L, int n)
322  {
323  if (lua_istable(L, n))
324  {
325  T res;
326  for (int i = 1, i_end = lua_rawlen(L, n); i <= i_end; ++i)
327  {
328  lua_rawgeti(L, n, i);
329  // By using insert instead of push_back, it magically "just works" for sets too.
330  res.insert(res.end(), lua_check_impl::lua_check<std::decay_t<typename T::reference>>(L, -1));
331  lua_pop(L, 1);
332  }
333  return res;
334  }
335  else
336  {
337  luaL_argerror(L, n, "Table expected");
338  throw "luaL_argerror returned"; //shouldn't happen, luaL_argerror always throws.
339  }
340  }
341 
342  //also accepts things like std::vector<int>() | std::adaptors::transformed(..)
343  template<typename T>
344  std::enable_if_t<
345  is_container<T>::value && !std::is_same_v<T, std::string> && !std::is_same_v<T, std::string_view> && !is_map<T>::value
346  , void
347  >
348  lua_push(lua_State * L, const T& list )
349  {
350  // NOTE: T might be some boost::iterator_range type where size might be < 0. (unfortunately in this case size() does not return T::size_type)
351  assert(list.size() >= 0);
352  lua_createtable(L, list.size(), 0);
353  int i = 1;
354  for(typename T::const_iterator iter = list.begin(); iter != list.end(); ++iter) {
355  lua_check_impl::lua_push<std::decay_t<typename T::reference>>(L, *iter);
356  lua_rawseti(L, -2, i++);
357  }
358  }
359 
360  //accepts std::map TODO: add a check function for that
361  template<typename T>
362  std::enable_if_t<is_map<T>::value, void>
363  lua_push(lua_State * L, const T& map )
364  {
365  lua_newtable(L);
366  for(const typename T::value_type& pair : map)
367  {
368  lua_check_impl::lua_push<std::decay_t<typename T::key_type>>(L, pair.first);
369  lua_check_impl::lua_push<std::decay_t<typename T::mapped_type>>(L, pair.second);
370  lua_settable(L, -3);
371  }
372  }
373 
374  // enum_base
375  template<typename T>
376  typename T::type
377  lua_check(lua_State *L, int n)
378  {
379  std::string str = lua_check_impl::lua_check<std::string>(L, n);
380  utils::optional<typename T::type> val = T::get_enum(str);
381  if(!val) {
382  luaL_argerror(L, n, ("cannot convert " + str + " to enum.").c_str());
383  }
384  return *val;
385  }
386 
387  //optional
388  template<typename T>
389  std::enable_if_t<is_instance_of_v<T, utils::optional>, T>
390  lua_check(lua_State *L, int n)
391  {
392  if(lua_isnoneornil(L, n)) {
393  return T();
394  }
395  return lua_check_impl::lua_check<typename T::value_type>(L, n);
396  }
397 
398  template<typename T>
399  std::enable_if_t<is_instance_of_v<T, utils::optional>, void>
400  lua_push(lua_State *L, const T& opt)
401  {
402  if(opt) {
403  lua_check_impl::lua_push<typename T::value_type>(L, *opt);
404  } else {
405  lua_pushnil(L);
406  }
407  }
408 }
409 
410 template<typename T>
411 std::decay_t<T> lua_check(lua_State *L, int n)
412 {
413  //remove possible const& to make life easier for the impl namespace.
414  return lua_check_impl::lua_check<std::decay_t<T>>(L, n);
415 }
416 
417 template<typename T>
418 std::decay_t<T> lua_to_or_default(lua_State *L, int n, const T& def)
419 {
420  //remove possible const& to make life easier for the impl namespace.
421  return lua_check_impl::lua_to_or_default<std::decay_t<T>>(L, n, def);
422 }
423 
424 template<typename T>
425 void lua_push(lua_State *L, const T& val)
426 {
427  return lua_check_impl::lua_push<std::decay_t<T>>(L, val);
428 }
429 
430 /**
431  * returns t[k] where k is the table at index @a index and k is @a k or @a def if it is not convertible to the correct type.
432  *
433  */
434 template<typename T>
435 std::decay_t<T> luaW_table_get_def(lua_State *L, int index, std::string_view k, const T& def)
436 {
437  if(!lua_istable(L, index)) {
438  luaL_argerror(L, index, "table expected");
439  }
440  if(index < 0) {
441  //with the next lua_pushstring negative indicies will no longer be correct otherwise.
442  --index;
443  }
444  lua_pushlstring(L, k.data(), k.size());
445  lua_gettable(L, index);
446  if(lua_isnoneornil(L, -1)) {
447  lua_pop(L, 1);
448  return def;
449  }
450  T res = lua_check_impl::lua_to_or_default<std::decay_t<T>>(L, -1, def);
451  lua_pop(L, 1);
452  return res;
453 }
454 
455 
456 template<typename T>
457 void luaW_table_set(lua_State *L, int index, std::string_view k, const T& value)
458 {
459  if(!lua_istable(L, index)) {
460  luaL_argerror(L, index, "table expected");
461  }
462 
463  index = lua_absindex(L, index);
464  lua_pushlstring(L, k.data(), k.size());
465  lua_push(L, value);
466  lua_settable(L, index);
467 }
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:163
Base class for all widgets.
Definition: widget.hpp:55
A variable-expanding proxy for the config class.
Definition: variable.hpp:45
static vconfig unconstructed_vconfig()
This is just a wrapper for the default constructor; it exists for historical reasons and to make it c...
Definition: variable.cpp:152
std::size_t i
Definition: function.cpp:1023
void luaW_pushconfig(lua_State *L, const config &cfg)
Converts a config object to a Lua table pushed at the top of the stack.
Definition: lua_common.cpp:837
config luaW_checkconfig(lua_State *L, int index)
Converts an optional table or vconfig to a config object.
Definition: lua_common.cpp:927
void luaW_pushlocation(lua_State *L, const map_location &ml)
Converts a map location object to a Lua table pushed at the top of the stack.
Definition: lua_common.cpp:740
void luaW_pushtstring(lua_State *L, const t_string &v)
Pushes a t_string on the top of the stack.
Definition: lua_common.cpp:545
bool luaW_tovconfig(lua_State *L, int index, vconfig &vcfg)
Gets an optional vconfig from either a table or a userdata.
Definition: lua_common.cpp:944
void luaW_pushvconfig(lua_State *L, const vconfig &cfg)
Pushes a vconfig on the top of the stack.
Definition: lua_common.cpp:539
bool luaW_toboolean(lua_State *L, int n)
Definition: lua_common.cpp:998
std::string_view luaW_tostring(lua_State *L, int index)
std::string_view luaW_tostring_or_default(lua_State *L, int index, std::string_view def)
bool luaW_toconfig(lua_State *L, int index, config &cfg)
Converts an optional table or vconfig to a config object.
Definition: lua_common.cpp:849
vconfig luaW_checkvconfig(lua_State *L, int index, bool allow_missing)
Gets an optional vconfig from either a table or a userdata.
Definition: lua_common.cpp:971
bool luaW_tolocation(lua_State *L, int index, map_location &loc)
Converts an optional table or pair of integers to a map location object.
Definition: lua_common.cpp:751
map_location luaW_checklocation(lua_State *L, int index)
Converts an optional table or pair of integers to a map location object.
Definition: lua_common.cpp:800
t_string luaW_checktstring(lua_State *L, int index)
Converts a scalar to a translatable string.
Definition: lua_common.cpp:635
void luaW_pushwidget(lua_State *L, gui2::widget &w)
Definition: lua_widget.cpp:34
std::enable_if_t< std::is_same_v< T, lua_index_raw >, lua_index_raw > lua_check(lua_State *L, int n)
Definition: push_check.hpp:74
std::enable_if_t< std::is_same_v< T, lua_index_raw >, lua_index_raw > lua_to_or_default(lua_State *L, int n, const T &)
Definition: push_check.hpp:80
constexpr bool is_instance_of_v
Definition: push_check.hpp:49
std::enable_if_t< std::is_same_v< T, lua_index_raw >, void > lua_push(lua_State *L, lua_index_raw n)
Definition: push_check.hpp:86
std::size_t index(const std::string &str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:70
std::decay_t< T > lua_to_or_default(lua_State *L, int n, const T &def)
Definition: push_check.hpp:418
std::decay_t< T > lua_check(lua_State *L, int n)
Definition: push_check.hpp:411
void lua_push(lua_State *L, const T &val)
Definition: push_check.hpp:425
std::decay_t< T > luaW_table_get_def(lua_State *L, int index, std::string_view k, const T &def)
returns t[k] where k is the table at index index and k is k or def if it is not convertible to the co...
Definition: push_check.hpp:435
void luaW_table_set(lua_State *L, int index, std::string_view k, const T &value)
Definition: push_check.hpp:457
lua_index_raw(int i)
Definition: push_check.hpp:31
lua_index_raw(lua_State *L)
Definition: push_check.hpp:32
Encapsulates the map of the game.
Definition: location.hpp:44
static map_location::DIRECTION n
#define d