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