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