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