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