The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
lua_common.hpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2014 - 2025
3 by Chris Beck <render787@gmail.com>
4 Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY.
12
13 See the COPYING file for more details.
14*/
15
16/**
17 * Common callbacks and functions to manipulate config, vconfig, tstring
18 * in lua, and macros to get them from the stack.
19 */
20
21#pragma once
22
23struct lua_State;
24class t_string;
25class vconfig;
26
27#include "config.hpp"
28#include "variable_info.hpp"
29#include "map/location.hpp"
30
31#include <string>
32#include <string_view>
33#include <vector>
34
35namespace lua_common {
36 int intf_textdomain(lua_State *L);
37 int intf_tovconfig(lua_State* L);
38
39 std::string register_gettext_metatable(lua_State *L);
40 std::string register_tstring_metatable(lua_State *L);
41 std::string register_vconfig_metatable(lua_State *L);
42
43}
44
45/**
46 * Shallow wrapper around lua_geti which pops the top variable from the
47 * Lua stack when destroyed. This is meant to be used in local contexts
48 * for quick cleanup of the stack variable.
49 *
50 * @todo support different get* functions?
51 */
53{
54public:
55 scoped_lua_argument(lua_State* L, int arg_index);
56 scoped_lua_argument(lua_State* L, int value_index, int arg_index);
57
59
62
63private:
64 lua_State* const state_;
65};
66
67void* operator new(std::size_t sz, lua_State *L, int nuv = 0);
68void operator delete(void* p, lua_State *L, int nuv);
69
70/**
71 * Efficiently creates new LUA "named tuples" with the specified field
72 * names. Use the @a push method to create a new empty tuple instance
73 * at the top of the stack.
74 *
75 * All tuples have some fixed layout and the total set of layouts is
76 * small (10-ish). So, we simply store the meta table the LUA registry
77 * once and then keep reusing it. Saves tons of construction effort.
78 * For maximum reuse, you should create static instances of this class.
79 *
80 * @note It is perfectly fine to have multiple instances of this
81 * class produce the same named tuple structure; the registry mechanism
82 * will ensure that there is a single common metatable not all of them.
83 */
85{
86public:
87
88 lua_named_tuple_builder(const std::vector<std::string>& names);
89
90 /**
91 * Push an empty "named tuple" onto the stack.
92 * A named tuple is an array where each index can also be accessed by name.
93 * Once it's pushed, you can set the elements, eg with lua_rawseti.
94 */
95 void push(lua_State *L) const;
96
97private:
98
99 static int impl_get(lua_State* L);
100 static int impl_set(lua_State* L);
101 static int impl_dir(lua_State* L);
102 static int impl_tostring(lua_State* L);
103 static int impl_compare(lua_State* L);
104
105 static std::string build_key(const std::vector<std::string>& names);
106
107 static constexpr std::string_view base_name_ = "named tuple";
108
109 const std::vector<std::string> names_;
110 const std::string key_;
111};
112
113/**
114 * Like luaL_getmetafield, but returns false if key is an empty string
115 * or begins with two underscores.
116 */
117bool luaW_getmetafield(lua_State *L, int idx, const char* key);
118
119/**
120 * Pushes a vconfig on the top of the stack.
121 */
122void luaW_pushvconfig(lua_State *L, const vconfig& cfg);
123
124/**
125 * Pushes a t_string on the top of the stack.
126 */
127void luaW_pushtstring(lua_State *L, const t_string& v);
128
129/**
130 * Converts an attribute value into a Lua object pushed at the top of the stack.
131 */
132void luaW_pushscalar(lua_State *L, const config::attribute_value& v);
133
134/**
135 * Converts the value at the top of the stack to an attribute value
136 */
137bool luaW_toscalar(lua_State *L, int index, config::attribute_value& v);
138
139/**
140 * Converts a scalar to a translatable string.
141 */
142bool luaW_totstring(lua_State *L, int index, t_string &str);
143
144/**
145 * Converts a scalar to a translatable string.
146 */
147t_string luaW_checktstring(lua_State *L, int index);
148
149/*
150 * Test if a scalar is either a plain or translatable string.
151 * Also returns true if it's a number since that's convertible to string.
152 */
153bool luaW_iststring(lua_State* L, int index);
154
155/**
156 * Converts a config object to a Lua table.
157 * The destination table should be at the top of the stack on entry. It is
158 * still at the top on exit.
159 */
160void luaW_filltable(lua_State *L, const config& cfg);
161
162/**
163 * Get the keys of a "named tuple" from the stack.
164 * Returns an empty array if the stack element is not a named tuple.
165 */
166std::vector<std::string> luaW_to_namedtuple(lua_State* L, int idx);
167
168/**
169 * Converts a map location object to a Lua table pushed at the top of the stack.
170 */
171void luaW_pushlocation(lua_State *L, const map_location& loc);
172
173/**
174 * Converts an optional table or pair of integers to a map location object.
175 * @param L the pointer to the lua interpreter.
176 * @param index stack position of the table or first integer.
177 * @param loc the location to write to.
178 * @return false if a map location couldn't be matched.
179 */
180bool luaW_tolocation(lua_State *L, int index, map_location &loc);
181
182/**
183 * Converts an optional table or pair of integers to a map location object.
184 * @note If a pair of integers was found, the first one will be removed
185 * from the stack when the function returns.
186 */
187map_location luaW_checklocation(lua_State *L, int index);
188
189/**
190 * Converts a set of map locations to a Lua table pushed at the top of the stack.
191 */
192int luaW_push_locationset(lua_State* L, const std::set<map_location>& locs);
193
194/**
195 * Converts a table of integer pairs to a set of map location objects.
196 */
197std::set<map_location> luaW_check_locationset(lua_State* L, int idx);
198
199/**
200 * Converts a config object to a Lua table pushed at the top of the stack.
201 */
202void luaW_pushconfig(lua_State *L, const config& cfg);
203
204/**
205 * Converts an optional table or vconfig to a config object.
206 * @param L the pointer to the lua interpreter.
207 * @param index stack position of the table.
208 * @param cfg the config to write the data to.
209 * @return false if some attributes had not the proper type.
210 * @note If the table has holes in the integer keys or floating-point keys,
211 * some keys will be ignored and the error will go undetected.
212 */
213bool luaW_toconfig(lua_State *L, int index, config &cfg);
214
215/**
216 * Converts an optional table or vconfig to a config object.
217 */
218config luaW_checkconfig(lua_State *L, int index);
219
220/**
221 * Gets an optional vconfig from either a table or a userdata.
222 * @return false in case of failure.
223 */
224bool luaW_tovconfig(lua_State *L, int index, vconfig &vcfg);
225
226/**
227 * Gets an optional vconfig from either a table or a userdata.
228 * @param L the pointer to the lua interpreter.
229 * @param index the location in the current lua execution stack to look at.
230 * @param allow_missing true if missing values are allowed; the function
231 * then returns an unconstructed vconfig.
232 */
233vconfig luaW_checkvconfig(lua_State *L, int index, bool allow_missing = false);
234
235/**
236 * Like the two-argument version, but if it was a vconfig, also
237 * returns a pointer to that vconfig.
238 */
239config luaW_checkconfig(lua_State *L, int index, const vconfig*& vcfg);
240
241/**
242 * Pushes the value found by following the variadic names (char *), if the
243 * value is not nil.
244 * @return true if an element was pushed.
245 */
246bool luaW_getglobal(lua_State *L, const std::vector<std::string>& path);
247
248/**
249 * Pushes the value found by following the variadic names (char *), if the
250 * value is not nil.
251 * @return true if an element was pushed.
252 */
253template<typename... T>
254bool luaW_getglobal(lua_State *L, T... path) {
255 return luaW_getglobal(L, std::vector<std::string> {path...} );
256}
257
258bool luaW_toboolean(lua_State *L, int n);
259
260
261bool luaW_pushvariable(lua_State *L, variable_access_const& v);
262
263bool luaW_checkvariable(lua_State *L, variable_access_create& v, int n);
264
265bool luaW_tableget(lua_State *L, int index, const char* key);
266
267std::string_view luaW_tostring(lua_State *L, int index);
268std::string_view luaW_tostring_or_default(lua_State *L, int index, std::string_view def = std::string_view());
269
270/**
271 * Displays a message in the chat window.
272 */
273void chat_message(const std::string& caption, const std::string& msg);
274
275/**
276 * Calls a Lua function stored below its @a nArgs arguments at the top of the stack.
277 * @param L the pointer to the lua interpreter.
278 * @param nArgs
279 * @param nRets LUA_MULTRET for unbounded return values.
280 * @param allow_wml_error controls where any stack traces are output.
281 * @return true if the call was successful and @a nRets return values are available.
282 */
283bool luaW_pcall(lua_State *L, int nArgs, int nRets, bool allow_wml_error = false);
284
285// Don't use these directly
286void push_error_handler(lua_State *L);
287int luaW_pcall_internal(lua_State *L, int nArgs, int nRets);
288
289int luaW_type_error(lua_State *L, int narg, const char *tname);
290int luaW_type_error(lua_State *L, int narg, const char* kpath, const char *tname);
291
292struct luaW_PrintStack { lua_State* L; };
293luaW_PrintStack luaW_debugstack(lua_State* L);
294std::ostream& operator<<(std::ostream& os, const luaW_PrintStack&);
295
296#define deprecate_attrib(name, prefix, level, version, msg) deprecated_message(prefix "." name, DEP_LEVEL::level, version, msg)
297
298#define return_deprecated_attrib(type_macro, name, accessor, prefix, level, version, msg) \
299 type_macro(name, ( \
300 deprecate_attrib(name, prefix, level, version, msg), \
301 accessor \
302 ))
303
304#define return_tstring_attrib(name, accessor) \
305do { \
306 if (strcmp(m, (name)) == 0) { \
307 luaW_pushtstring(L, (accessor)); \
308 return 1; \
309 } \
310} while(false)
311#define return_tstring_attrib_deprecated(name, prefix, level, version, msg, accessor) \
312 return_deprecated_attrib(return_tstring_attrib, name, accessor, prefix, level, version, msg)
313
314#define return_cstring_attrib(name, accessor) \
315do { \
316 if (strcmp(m, (name)) == 0) { \
317 lua_pushstring(L, (accessor)); \
318 return 1; \
319 } \
320} while(false)
321#define return_cstring_attrib_deprecated(name, prefix, level, version, msg, accessor) \
322 return_deprecated_attrib(return_cstring_attrib, name, accessor, prefix, level, version, msg)
323
324#define return_string_attrib(name, accessor) \
325do { \
326 if (strcmp(m, (name)) == 0) { \
327 const std::string& str = (accessor); \
328 lua_pushlstring(L, str.c_str(), str.length()); \
329 return 1; \
330 } \
331} while(false)
332#define return_string_attrib_deprecated(name, prefix, level, version, msg, accessor) \
333 return_deprecated_attrib(return_string_attrib, name, accessor, prefix, level, version, msg)
334
335#define return_int_attrib(name, accessor) \
336do { \
337 if (strcmp(m, (name)) == 0) { \
338 lua_pushinteger(L, (accessor)); \
339 return 1; \
340 } \
341} while(false)
342#define return_int_attrib_deprecated(name, prefix, level, version, msg, accessor) \
343 return_deprecated_attrib(return_int_attrib, name, accessor, prefix, level, version, msg)
344
345#define return_float_attrib(name, accessor) \
346do { \
347 if (strcmp(m, (name)) == 0) { \
348 lua_pushnumber(L, (accessor)); \
349 return 1; \
350 } \
351} while(false)
352#define return_float_attrib_deprecated(name, prefix, level, version, msg, accessor) \
353 return_deprecated_attrib(return_float_attrib, name, accessor, prefix, level, version, msg)
354
355#define return_bool_attrib(name, accessor) \
356do { \
357 if (strcmp(m, (name)) == 0) { \
358 lua_pushboolean(L, (accessor)); \
359 return 1; \
360 } \
361} while(false)
362#define return_bool_attrib_deprecated(name, prefix, level, version, msg, accessor) \
363 return_deprecated_attrib(return_bool_attrib, name, accessor, prefix, level, version, msg)
364
365#define return_cfg_attrib(name, accessor) \
366do { \
367 if (strcmp(m, (name)) == 0) { \
368 config cfg; \
369 {accessor;} \
370 luaW_pushconfig(L, cfg); \
371 return 1; \
372 } \
373} while(false)
374#define return_cfg_attrib_deprecated(name, prefix, level, version, msg, accessor) \
375 return_cfg_attrib(name, deprecate_attrib(name, prefix, level, version, msg); accessor)
376
377#define return_cfgref_attrib(name, accessor) \
378do { \
379 if (strcmp(m, (name)) == 0) { \
380 luaW_pushconfig(L, (accessor)); \
381 return 1; \
382 } \
383} while(false)
384#define return_cfgref_attrib_deprecated(name, prefix, level, version, msg, accessor) \
385 return_deprecated_attrib(return_cfgref_attrib, name, accessor, prefix, level, version, msg)
386
387#define return_vector_string_attrib(name, accessor) \
388do { \
389 if (strcmp(m, (name)) == 0) { \
390 const std::vector<std::string>& vector = (accessor); \
391 lua_createtable(L, vector.size(), 0); \
392 int i = 1; \
393 for (const std::string& s : vector) { \
394 lua_pushlstring(L, s.c_str(), s.length()); \
395 lua_rawseti(L, -2, i); \
396 ++i; \
397 } \
398 return 1; \
399 } \
400} while(false)
401#define return_vector_string_attrib_deprecated(name, prefix, level, version, msg, accessor) \
402 return_deprecated_attrib(return_vector_string_attrib, name, accessor, prefix, level, version, msg)
403
404#define modify_tstring_attrib(name, accessor) \
405do { \
406 if (strcmp(m, (name)) == 0) { \
407 t_string value = luaW_checktstring(L, 3); \
408 {accessor;} \
409 return 0; \
410 } \
411} while(false)
412#define modify_tstring_attrib_deprecated(name, prefix, level, version, msg, accessor) \
413 modify_tstring_attrib(name, deprecate_attrib(name, prefix, level, version, msg); accessor)
414
415#define modify_string_attrib(name, accessor) \
416do { \
417 if (strcmp(m, (name)) == 0) { \
418 const char *value = luaL_checkstring(L, 3); \
419 {accessor;} \
420 return 0; \
421 } \
422} while(false)
423#define modify_string_attrib_deprecated(name, prefix, level, version, msg, accessor) \
424 modify_string_attrib(name, deprecate_attrib(name, prefix, level, version, msg); accessor)
425
426#define modify_int_attrib(name, accessor) \
427do { \
428 if (strcmp(m, (name)) == 0) { \
429 int value = static_cast<int>(luaL_checknumber(L, 3)); \
430 {accessor;} \
431 return 0; \
432 } \
433} while(false)
434#define modify_int_attrib_deprecated(name, prefix, level, version, msg, accessor) \
435 modify_int_attrib(name, deprecate_attrib(name, prefix, level, version, msg); accessor)
436
437#define modify_int_attrib_check_range(name, accessor, allowed_min, allowed_max) \
438do { \
439 if (strcmp(m, (name)) == 0) { \
440 int value = static_cast<int>(luaL_checknumber(L, 3)); \
441 if (value < (allowed_min) || (allowed_max) < value) return luaL_argerror(L, 3, "out of bounds"); \
442 {accessor;} \
443 return 0; \
444 } \
445} while(false)
446#define modify_int_attrib_check_range_deprecated(name, prefix, level, version, msg, accessor, allowed_min, allowed_max) \
447 modify_int_attrib_check_range(name, deprecate_attrib(name, prefix, level, version, msg); accessor, allowed_min, allowed_max)
448
449#define modify_float_attrib(name, accessor) \
450do { \
451 if (strcmp(m, (name)) == 0) { \
452 lua_Number value = luaL_checknumber(L, 3); \
453 {accessor;} \
454 return 0; \
455 } \
456} while(false)
457#define modify_float_attrib_deprecated(name, prefix, level, version, msg, accessor) \
458 modify_float_attrib(name, deprecate_attrib(name, prefix, level, version, msg); accessor)
459
460#define modify_float_attrib_check_range(name, accessor, allowed_min, allowed_max) \
461do { \
462 if (strcmp(m, (name)) == 0) { \
463 lua_Number value = luaL_checknumber(L, 3); \
464 if (value < (allowed_min) || (allowed_max) < value) return luaL_argerror(L, 3, "out of bounds"); \
465 {accessor;} \
466 return 0; \
467 } \
468} while(false)
469#define modify_float_attrib_check_range_deprecated(name, prefix, level, version, msg, accessor, allowed_min, allowed_max) \
470 modify_float_attrib_check_range(name, deprecate_attrib(name, prefix, level, version, msg); accessor, allowed_min, allowed_max)
471
472#define modify_bool_attrib(name, accessor) \
473do { \
474 if (strcmp(m, (name)) == 0) { \
475 bool value = luaW_toboolean(L, 3); \
476 {accessor;} \
477 return 0; \
478 } \
479} while(false)
480#define modify_bool_attrib_deprecated(name, prefix, level, version, msg, accessor) \
481 modify_bool_attrib(name, deprecate_attrib(name, prefix, level, version, msg); accessor)
482
483#define modify_cfg_attrib(name, accessor) \
484do { \
485 if (strcmp(m, (name)) == 0) { \
486 const config& cfg = luaW_checkconfig(L, 3); \
487 {accessor;} \
488 return 0; \
489 } \
490} while(false)
491#define modify_cfg_attrib_deprecated(name, prefix, level, version, msg, accessor) \
492 modify_cfg_attrib(name, deprecate_attrib(name, prefix, level, version, msg); accessor)
493
494#define modify_vector_string_attrib(name, accessor) \
495do { \
496 if (strcmp(m, (name)) == 0) { \
497 std::vector<std::string> value; \
498 char const* message = "table with unnamed indices holding strings expected"; \
499 if (!lua_istable(L, 3)) return luaL_argerror(L, 3, message); \
500 unsigned length = lua_rawlen(L, 3); \
501 for (unsigned i = 1; i <= length; ++i) { \
502 lua_rawgeti(L, 3, i); \
503 char const* string = lua_tostring(L, 4); \
504 if(!string) return luaL_argerror(L, 2 + i, message); \
505 value.push_back(string); \
506 lua_pop(L, 1); \
507 } \
508 {accessor;} \
509 return 0; \
510 } \
511} while(false)
512#define modify_vector_string_attrib_deprecated(name, prefix, level, version, msg, accessor) \
513 modify_vector_string_attrib(name, deprecate_attrib(name, prefix, level, version, msg); accessor)
map_location loc
Definition move.cpp:172
std::vector< std::string > names
Variant for storing WML attributes.
A config object defines a single node in a WML file, with access to child nodes.
Definition config.hpp:157
Efficiently creates new LUA "named tuples" with the specified field names.
static int impl_set(lua_State *L)
static std::string build_key(const std::vector< std::string > &names)
static int impl_tostring(lua_State *L)
static int impl_compare(lua_State *L)
void push(lua_State *L) const
Push an empty "named tuple" onto the stack.
static int impl_dir(lua_State *L)
const std::vector< std::string > names_
static int impl_get(lua_State *L)
static constexpr std::string_view base_name_
const std::string key_
Shallow wrapper around lua_geti which pops the top variable from the Lua stack when destroyed.
scoped_lua_argument & operator=(const scoped_lua_argument &)=delete
scoped_lua_argument(const scoped_lua_argument &)=delete
lua_State *const state_
Additional functionality for a non-const variable_info.
Information on a WML variable.
A variable-expanding proxy for the config class.
Definition variable.hpp:45
Definitions for the interface to Wesnoth Markup Language (WML).
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.
int luaW_pcall_internal(lua_State *L, int nArgs, int nRets)
void luaW_filltable(lua_State *L, const config &cfg)
Converts a config object to a Lua table.
void chat_message(const std::string &caption, const std::string &msg)
Displays a message in the chat window.
void push_error_handler(lua_State *L)
vconfig luaW_checkvconfig(lua_State *L, int index, bool allow_missing=false)
Gets an optional vconfig from either a table or a userdata.
bool luaW_iststring(lua_State *L, int index)
config luaW_checkconfig(lua_State *L, int index)
Converts an optional table or vconfig to a config object.
void luaW_pushtstring(lua_State *L, const t_string &v)
Pushes a t_string on the top of the stack.
bool luaW_pushvariable(lua_State *L, variable_access_const &v)
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)
int luaW_type_error(lua_State *L, int narg, const char *tname)
bool luaW_toscalar(lua_State *L, int index, config::attribute_value &v)
Converts the value at the top of the stack to an attribute value.
std::string_view luaW_tostring(lua_State *L, int index)
bool luaW_getmetafield(lua_State *L, int idx, const char *key)
Like luaL_getmetafield, but returns false if key is an empty string or begins with two underscores.
void luaW_pushscalar(lua_State *L, const config::attribute_value &v)
Converts an attribute value into a Lua object pushed at the top of the stack.
bool luaW_totstring(lua_State *L, int index, t_string &str)
Converts a scalar to a translatable string.
void luaW_pushlocation(lua_State *L, const map_location &loc)
Converts a map location object to a Lua table pushed at the top of the stack.
std::vector< std::string > luaW_to_namedtuple(lua_State *L, int idx)
Get the keys of a "named tuple" from the stack.
bool luaW_tableget(lua_State *L, int index, const char *key)
std::ostream & operator<<(std::ostream &os, const luaW_PrintStack &)
bool luaW_checkvariable(lua_State *L, variable_access_create &v, int n)
bool luaW_toconfig(lua_State *L, int index, config &cfg)
Converts an optional table or vconfig to a config object.
bool luaW_pcall(lua_State *L, int nArgs, int nRets, bool allow_wml_error=false)
Calls a Lua function stored below its nArgs arguments at the top of the stack.
int luaW_push_locationset(lua_State *L, const std::set< map_location > &locs)
Converts a set of map locations to a Lua table pushed at the top of the stack.
std::set< map_location > luaW_check_locationset(lua_State *L, int idx)
Converts a table of integer pairs to a set of map location objects.
std::string_view luaW_tostring_or_default(lua_State *L, int index, std::string_view def=std::string_view())
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.
luaW_PrintStack luaW_debugstack(lua_State *L)
bool luaW_getglobal(lua_State *L, const std::vector< std::string > &path)
Pushes the value found by following the variadic names (char *), if the value is not nil.
t_string luaW_checktstring(lua_State *L, int index)
Converts a scalar to a translatable string.
int intf_textdomain(lua_State *L)
Creates an interface for gettext.
std::string register_gettext_metatable(lua_State *L)
Adds the gettext metatable.
std::string register_vconfig_metatable(lua_State *L)
Adds the vconfig metatable.
std::string register_tstring_metatable(lua_State *L)
Adds the tstring metatable.
int intf_tovconfig(lua_State *L)
Creates a vconfig containing the WML table.
lua_State * L
Encapsulates the map of the game.
Definition location.hpp:46
mock_party p
static map_location::direction n