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