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