The Battle for Wesnoth  1.19.0-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 "global.hpp"
22 #include "tstring.hpp"
23 #include "map/location.hpp"
24 
25 #include <cassert>
26 #include <string_view>
27 #include <type_traits>
28 
29 struct lua_index_raw { int index; };
30 
31 namespace lua_check_impl
32 {
33  template<typename T, typename T2 = void>
34  struct is_container : std::false_type {};
35 
36  template<typename T>
37  struct is_container<T, std::void_t<
38  typename std::decay_t<T>::value_type,
39  typename std::decay_t<T>::iterator,
40  typename std::decay_t<T>::size_type,
41  typename std::decay_t<T>::reference>
42  > : std::true_type {};
43 
44  template<typename T, typename T2 = void>
45  struct is_map : std::false_type {};
46 
47  template<typename T>
48  struct is_map<T, std::void_t<
49  typename std::decay_t<T>::key_type,
50  typename std::decay_t<T>::mapped_type>
51  > : std::true_type {};
52 
53  template<typename T, typename T2 = void>
54  struct is_pair : std::false_type {};
55 
56  template<typename T>
57  struct is_pair<T, std::void_t<
58  typename std::decay_t<T>::first_type,
59  typename std::decay_t<T>::second_type>
60  > : std::true_type {};
61 
62  template<typename T>
63  std::enable_if_t<std::is_same_v<T, lua_index_raw>, lua_index_raw>
64  lua_check(lua_State *L, int n)
65  {
66  UNUSED(L);
67  return lua_index_raw{ n };
68  }
69  template<typename T>
70  std::enable_if_t<std::is_same_v<T, lua_index_raw>, lua_index_raw>
71  lua_to_or_default(lua_State *L, int n, const T& /*def*/)
72  {
73  UNUSED(L);
74  return lua_index_raw{ n };
75  }
76 
77  //std::string
78  template<typename T>
79  std::enable_if_t<std::is_same_v<T, std::string>, std::string>
80  lua_check(lua_State *L, int n)
81  {
82  return luaL_checkstring(L, n);
83  }
84  template<typename T>
85  std::enable_if_t<std::is_same_v<T, std::string>, std::string>
86  lua_to_or_default(lua_State *L, int n, const T& def)
87  {
88  return luaL_optstring(L, n, def.c_str());
89  }
90  template<typename T>
91  std::enable_if_t<std::is_same_v<T, std::string>, void>
92  lua_push(lua_State *L, const T& val)
93  {
94  lua_pushlstring(L, val.c_str(), val.size());
95  }
96 
97  //std::string_view
98  template<typename T>
99  std::enable_if_t<std::is_same_v<T, std::string_view>, std::string_view>
100  lua_check(lua_State *L, int n)
101  {
102  return luaW_tostring(L, n);
103  }
104  template<typename T>
105  std::enable_if_t<std::is_same_v<T, std::string_view>, std::string_view>
106  lua_to_or_default(lua_State *L, int n, const T& def)
107  {
108  return luaW_tostring_or_default(L, n, def);
109  }
110  template<typename T>
111  std::enable_if_t<std::is_same_v<T, std::string_view>, void>
112  lua_push(lua_State *L, const T& val)
113  {
114  lua_pushlstring(L, val.data(), val.size());
115  }
116 
117  //config
118  template<typename T>
119  std::enable_if_t<std::is_same_v<T, config>, config>
120  lua_check(lua_State *L, int n)
121  {
122  return luaW_checkconfig(L, n);
123  }
124  template<typename T>
125  std::enable_if_t<std::is_same_v<T, config>, config>
126  lua_to_or_default(lua_State *L, int n, const T& def)
127  {
128  config cfg;
129  return luaW_toconfig(L, n, cfg) ? cfg : def;
130  }
131  template<typename T>
132  std::enable_if_t<std::is_same_v<T, config>, void>
133  lua_push(lua_State *L, const config& val)
134  {
135  luaW_pushconfig(L, val);
136  }
137 
138  //location
139  template<typename T>
140  std::enable_if_t<std::is_same_v<T, map_location>, map_location>
141  lua_check(lua_State *L, int n)
142  {
143  return luaW_checklocation(L, n);
144  }
145  template<typename T>
146  std::enable_if_t<std::is_same_v<T, map_location>, map_location>
147  lua_to_or_default(lua_State *L, int n, const T& def)
148  {
149  map_location res;
150  if (!luaW_tolocation(L, n, res)) {
151  return def;
152  }
153  return res;
154  }
155  template<typename T>
156  std::enable_if_t<std::is_same_v<T, map_location>, void>
157  lua_push(lua_State *L, const map_location& val)
158  {
159  luaW_pushlocation(L, val);
160  }
161 
162  //t_string
163  template<typename T>
164  std::enable_if_t<std::is_same_v<T, t_string>, t_string>
165  lua_check(lua_State *L, int n)
166  {
167  return luaW_checktstring(L, n);
168  }
169  template<typename T>
170  std::enable_if_t<std::is_same_v<T, t_string>, void>
171  lua_push(lua_State *L, const t_string& val)
172  {
173  luaW_pushtstring(L, val);
174  }
175 
176  //widget
177  //widget not suppored becasue lua_checek returns by value
178  template<typename T>
179  std::enable_if_t<std::is_same_v<T, gui2::widget>, void>
180  lua_push(lua_State *L, gui2::widget& val)
181  {
182  luaW_pushwidget(L, val);
183  }
184 
185  //bool
186  template<typename T>
187  std::enable_if_t<std::is_same_v<T, bool>, bool>
188  lua_check(lua_State *L, int n)
189  {
190  return luaW_toboolean(L, n);
191  }
192  template<typename T>
193  std::enable_if_t<std::is_same_v<T, bool>, bool>
194  lua_to_or_default(lua_State *L, int n, const T& /*def*/)
195  {
196  return luaW_toboolean(L, n);
197  }
198  template<typename T>
199  std::enable_if_t<std::is_same_v<T, bool>, void>
200  lua_push(lua_State *L, bool val)
201  {
202  lua_pushboolean(L, val);
203  }
204 
205  //double, float
206  template<typename T>
207  std::enable_if_t<std::is_floating_point_v<T>, T>
208  lua_check(lua_State *L, int n)
209  {
210  return luaL_checknumber(L, n);
211  }
212  template<typename T>
213  std::enable_if_t<std::is_floating_point_v<T>, T>
214  lua_to_or_default(lua_State *L, int n, const T& def)
215  {
216  int isnum;
217  lua_Number d = lua_tonumberx(L, n, &isnum);
218  if (!isnum) {
219  return def;
220  }
221  return d;
222  }
223  template<typename T>
224  std::enable_if_t<std::is_floating_point_v<T>, void>
225  lua_push(lua_State *L, T val)
226  {
227  lua_pushnumber(L, val);
228  }
229 
230  //integer types
231  template<typename T>
232  std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, bool>, T>
233  lua_check(lua_State *L, int n)
234  {
235  return luaL_checkinteger(L, n);
236  }
237  template<typename T>
238  std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, bool>, T>
239  lua_to_or_default(lua_State *L, int n, const T& def)
240  {
241  int isnum;
242  lua_Integer res = lua_tointegerx(L, n, &isnum);
243  if (!isnum) {
244  return def;
245  }
246  return res;
247  }
248 
249  template<typename T>
250  std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, bool>, void>
251  lua_push(lua_State *L, T val)
252  {
253  lua_pushinteger(L, val);
254  }
255 
256  //std::pair
257  //Not sure if the not_<is_const> is required; only (maybe) if std::map matches is_container
258  template<typename T>
259  std::enable_if_t<is_pair<T>::value && !std::is_const_v<typename T::first_type>, T>
260  lua_check(lua_State *L, int n)
261  {
262  T result;
263  if (lua_istable(L, n)) {
264  lua_rawgeti(L, n, 1);
265  result.first = lua_check<const typename T::first_type&>(L, -1);
266  lua_rawgeti(L, n, 2);
267  result.second = lua_check<const typename T::second_type&>(L, -1);
268  lua_pop(L, 2);
269  }
270  return result;
271  }
272  template<typename T>
273  std::enable_if_t<is_pair<T>::value && !std::is_const_v<typename T::first_type>, void>
274  lua_push(lua_State *L, const T& val)
275  {
276  lua_newtable(L);
277  lua_push<const typename T::first_type&>(L, val.first);
278  lua_rawseti(L, -2, 1);
279  lua_push<const typename T::second_type&>(L, val.second);
280  lua_rawseti(L, -2, 2);
281  }
282 
283  //std::vector and similar but not std::string
284  template<typename T>
285  std::enable_if_t<is_container<T>::value && !std::is_same_v<T, std::string> && !std::is_same_v<T, std::string_view>, T>
286  lua_check(lua_State * L, int n)
287  {
288  if (lua_istable(L, n))
289  {
290  T res;
291  for (int i = 1, i_end = lua_rawlen(L, n); i <= i_end; ++i)
292  {
293  lua_rawgeti(L, n, i);
294  res.push_back(lua_check_impl::lua_check<std::decay_t<typename T::reference>>(L, -1));
295  lua_pop(L, 1);
296  }
297  return res;
298  }
299  else
300  {
301  luaL_argerror(L, n, "Table expected");
302  throw "luaL_argerror returned"; //shouldn't happen, luaL_argerror always throws.
303  }
304  }
305 
306  //also accepts things like std::vector<int>() | std::adaptors::transformed(..)
307  template<typename T>
308  std::enable_if_t<
309  is_container<T>::value && !std::is_same_v<T, std::string> && !std::is_same_v<T, std::string_view> && !is_map<T>::value
310  , void
311  >
312  lua_push(lua_State * L, const T& list )
313  {
314  // 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)
315  assert(list.size() >= 0);
316  lua_createtable(L, list.size(), 0);
317  int i = 1;
318  for(typename T::const_iterator iter = list.begin(); iter != list.end(); ++iter) {
319  lua_check_impl::lua_push<std::decay_t<typename T::reference>>(L, *iter);
320  lua_rawseti(L, -2, i++);
321  }
322  }
323 
324  //accepts std::map TODO: add a check function for that
325  template<typename T>
326  std::enable_if_t<is_map<T>::value, void>
327  lua_push(lua_State * L, const T& map )
328  {
329  lua_newtable(L);
330  for(const typename T::value_type& pair : map)
331  {
332  lua_check_impl::lua_push<std::decay_t<typename T::key_type>>(L, pair.first);
333  lua_check_impl::lua_push<std::decay_t<typename T::mapped_type>>(L, pair.second);
334  lua_settable(L, -3);
335  }
336  }
337 
338  // enum_base
339  template<typename T>
340  typename T::type
341  lua_check(lua_State *L, int n)
342  {
343  std::string str = lua_check_impl::lua_check<std::string>(L, n);
344  std::optional<typename T::type> val = T::get_enum(str);
345  if(!val) {
346  luaL_argerror(L, n, ("cannot convert " + str + " to enum.").c_str());
347  }
348  return *val;
349  }
350 }
351 
352 template<typename T>
353 typename T::type lua_enum_check(lua_State *L, int n)
354 {
355  return lua_check_impl::lua_check<T>(L, n);
356 }
357 
358 template<typename T>
359 std::decay_t<T> lua_check(lua_State *L, int n)
360 {
361  //remove possible const& to make life easier for the impl namespace.
362  return lua_check_impl::lua_check<std::decay_t<T>>(L, n);
363 }
364 
365 template<typename T>
366 std::decay_t<T> lua_to_or_default(lua_State *L, int n, const T& def)
367 {
368  //remove possible const& to make life easier for the impl namespace.
369  return lua_check_impl::lua_to_or_default<std::decay_t<T>>(L, n, def);
370 }
371 
372 template<typename T>
373 void lua_push(lua_State *L, const T& val)
374 {
375  return lua_check_impl::lua_push<std::decay_t<T>>(L, val);
376 }
377 
378 /**
379  * 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.
380  *
381  */
382 template<typename T>
383 std::decay_t<T> luaW_table_get_def(lua_State *L, int index, std::string_view k, const T& def)
384 {
385  if(!lua_istable(L, index)) {
386  luaL_argerror(L, index, "table expected");
387  }
388  if(index < 0) {
389  //with the next lua_pushstring negative indicies will no longer be correct otherwise.
390  --index;
391  }
392  lua_pushlstring(L, k.data(), k.size());
393  lua_gettable(L, index);
394  if(lua_isnoneornil(L, -1)) {
395  lua_pop(L, 1);
396  return def;
397  }
398  T res = lua_check_impl::lua_to_or_default<std::decay_t<T>>(L, -1, def);
399  lua_pop(L, 1);
400  return res;
401 }
402 
403 
404 template<typename T>
405 void luaW_table_set(lua_State *L, int index, std::string_view k, const T& value)
406 {
407  if(!lua_istable(L, index)) {
408  luaL_argerror(L, index, "table expected");
409  }
410 
411  index = lua_absindex(L, index);
412  lua_pushlstring(L, k.data(), k.size());
413  lua_push(L, value);
414  lua_settable(L, index);
415 }
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:159
Base class for all widgets.
Definition: widget.hpp:53
std::size_t i
Definition: function.cpp:968
#define UNUSED(x)
Definition: global.hpp:31
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:827
config luaW_checkconfig(lua_State *L, int index)
Converts an optional table or vconfig to a config object.
Definition: lua_common.cpp:917
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:730
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_toboolean(lua_State *L, int n)
Definition: lua_common.cpp:988
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:839
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:741
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:790
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:30
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:64
std::enable_if_t< std::is_same_v< T, std::string >, void > lua_push(lua_State *L, const T &val)
Definition: push_check.hpp:92
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:71
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:366
std::decay_t< T > lua_check(lua_State *L, int n)
Definition: push_check.hpp:359
void lua_push(lua_State *L, const T &val)
Definition: push_check.hpp:373
T::type lua_enum_check(lua_State *L, int n)
Definition: push_check.hpp:353
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:383
void luaW_table_set(lua_State *L, int index, std::string_view k, const T &value)
Definition: push_check.hpp:405
Encapsulates the map of the game.
Definition: location.hpp:38
static map_location::DIRECTION n
#define d