The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
variable.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2005 - 2025
3 by Philippe Plantier <ayin@anathas.org>
4 Copyright (C) 2003 by David White <dave@whitevine.net>
5 Part of the Battle for Wesnoth Project https://www.wesnoth.org/
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY.
13
14 See the COPYING file for more details.
15*/
16
17/**
18 * @file
19 * Manage WML-variables.
20 */
21
22#include "variable.hpp"
23
25#include "game_board.hpp"
26#include "game_data.hpp"
27#include "log.hpp"
28#include "resources.hpp"
29#include "units/unit.hpp"
30#include "units/map.hpp"
31#include "utils/general.hpp"
32#include "team.hpp"
33
34static lg::log_domain log_engine("engine");
35#define LOG_NG LOG_STREAM(info, log_engine)
36#define WRN_NG LOG_STREAM(warn, log_engine)
37#define ERR_NG LOG_STREAM(err, log_engine)
38namespace
39{
40 const config as_nonempty_range_default("_");
41 config::const_child_itors as_nonempty_range(const std::string& varname, const variable_set& vars)
42 {
44
45 if(!range.empty()) {
46 return range;
47 }
48
49 return as_nonempty_range_default.child_range("_");
50 }
51
52 // doxygen didn't like this as an anonymous struct
53 struct anon : public variable_set
54 {
55 config::attribute_value get_variable_const(const std::string&) const override
56 {
58 }
59 variable_access_const get_variable_access_read(const std::string& varname) const override
60 {
61 return variable_access_const(varname, config());
62 }
63 } null_variable_set;
64}
65
67 try {
69 return variable.as_scalar();
70 } catch(const invalid_variablename_exception&) {
71 ERR_NG << "invalid variablename " << id;
73 }
74}
75
79
81
83{
86 }
87 return &null_variable_set;
88}
89
91 cache_(), cfg_(&default_empty_config), variables_(try_get_gamedata())
92{
93}
94
95vconfig::vconfig(const config & cfg, const std::shared_ptr<const config> & cache) :
96 cache_(cache), cfg_(&cfg), variables_(try_get_gamedata())
97{
98}
99
100/**
101 * Constructor from a config, with an option to manage memory.
102 * @param[in] cfg The "WML source" of the vconfig being constructed.
103 * @param[in] manage_memory If true, a copy of @a cfg will be made, allowing the
104 * vconfig to safely persist after @a cfg is destroyed.
105 * If false, no copy is made, so @a cfg must be
106 * guaranteed to persist as long as the vconfig will.
107 * If in doubt, set to true; it is less efficient, but safe.
108 * @param[in] vars
109 * See also make_safe().
110 */
111vconfig::vconfig(const config &cfg, bool manage_memory, const variable_set* vars)
112 : cache_(manage_memory ? new config(cfg) : nullptr)
113 , cfg_(manage_memory ? cache_.get() : &cfg)
114 , variables_(vars ? vars : try_get_gamedata())
115{
116}
117
119 : cache_(), cfg_(&cfg), variables_(try_get_gamedata())
120{}
121
123 : cache_(new config(std::move(cfg))), cfg_(cache_.get()), variables_(try_get_gamedata())
124{}
125
126vconfig::vconfig(const config& cfg, const std::shared_ptr<const config> & cache, const variable_set& variables)
127 : cache_(cache), cfg_(&cfg), variables_(&variables)
128{}
129
130vconfig::vconfig(const config& cfg, const variable_set& variables)
131 : cache_(), cfg_(&cfg), variables_(&variables)
132{}
133
134/**
135 * Default destructor, but defined here for possibly faster compiles
136 * (templates sometimes can be rough on the compiler).
137 */
141
143{
144 static const config empty_config;
145 return vconfig(empty_config, false);
146}
147
148/**
149 * This is just a wrapper for the default constructor; it exists for historical
150 * reasons and to make it clear that default construction cannot be dereferenced
151 * (in contrast to an empty vconfig).
152 */
157
158/**
159 * Ensures that *this manages its own memory, making it safe for *this to
160 * outlive the config it was ultimately constructed from.
161 * It is perfectly safe to call this for a vconfig that already manages its memory.
162 * This does not work on a null() vconfig.
163 */
165{
166 // Nothing to do if we already manage our own memory.
167 if ( memory_managed() )
168 return *this;
169
170 // Make a copy of our config.
171 cache_.reset(new config(*cfg_));
172 // Use our copy instead of the original.
173 cfg_ = cache_.get();
174 return *this;
175}
176
178{
179 // Keeps track of insert_tag variables.
180 static std::set<std::string> vconfig_recursion;
181
182 config res;
183
184 for(const auto& [key, _] : cfg_->attribute_range()) {
185 res[key] = expand(key);
186 }
187
188 for(const auto [key, cfg] : cfg_->all_children_view())
189 {
190 if (key == "insert_tag") {
191 vconfig insert_cfg(cfg, *variables_);
192 std::string name = insert_cfg["name"];
193 std::string vname = insert_cfg["variable"];
194 if(!vconfig_recursion.insert(vname).second) {
195 throw recursion_error("vconfig::get_parsed_config() infinite recursion detected, aborting");
196 }
197 try
198 {
199 config::const_child_itors range = as_nonempty_range(vname, *variables_);
200 for (const config& ch : range)
201 {
202 res.add_child(name, vconfig(ch, *variables_).get_parsed_config());
203 }
204 }
206 {
207 res.add_child(name);
208 }
209 catch(const recursion_error &err) {
210 vconfig_recursion.erase(vname);
211 WRN_NG << err.message;
212 if(vconfig_recursion.empty()) {
213 res.add_child("insert_tag", insert_cfg.get_config());
214 } else {
215 // throw to the top [insert_tag] which started the recursion
216 throw;
217 }
218 }
219 vconfig_recursion.erase(vname);
220 } else {
221 res.add_child(key, vconfig(cfg, *variables_).get_parsed_config());
222 }
223 }
224 return res;
225}
226
227vconfig::child_list vconfig::get_children(const std::string& key_to_get) const
228{
230
231 for(const auto [key, cfg] : cfg_->all_children_view())
232 {
233 if (key == key_to_get) {
234 res.push_back(vconfig(cfg, cache_, *variables_));
235 } else if (key == "insert_tag") {
236 vconfig insert_cfg(cfg, *variables_);
237 if(insert_cfg["name"] == key_to_get)
238 {
239 try
240 {
241 config::const_child_itors range = as_nonempty_range(insert_cfg["variable"], *variables_);
242 for (const config& ch : range)
243 {
244 res.push_back(vconfig(ch, true, variables_));
245 }
246 }
248 {
249 res.push_back(empty_vconfig());
250 }
251 }
252 }
253 }
254 return res;
255}
256
257std::size_t vconfig::count_children(const std::string& key_to_count) const
258{
259 std::size_t n = 0;
260
261 for(const auto [key, cfg] : cfg_->all_children_view())
262 {
263 if (key == key_to_count) {
264 n++;
265 } else if (key == "insert_tag") {
266 vconfig insert_cfg(cfg, *variables_);
267 if(insert_cfg["name"] == key_to_count)
268 {
269 try
270 {
271 config::const_child_itors range = as_nonempty_range(insert_cfg["variable"], *variables_);
272 n += range.size();
273 }
275 {
276 n++;
277 }
278 }
279 }
280 }
281 return n;
282}
283
284/**
285 * Returns a child of *this whose key is @a key.
286 * If no such child exists, returns an unconstructed vconfig (use null() to test
287 * for this).
288 */
289vconfig vconfig::child(const std::string& key) const
290{
291 if (auto natural = cfg_->optional_child(key)) {
292 return vconfig(*natural, cache_, *variables_);
293 }
294 for (const config &ins : cfg_->child_range("insert_tag"))
295 {
296 vconfig insert_cfg(ins, *variables_);
297 if(insert_cfg["name"] == key)
298 {
299 try
300 {
301 config::const_child_itors range = as_nonempty_range(insert_cfg["variable"], *variables_);
302 return vconfig(range.front(), true, variables_);
303 }
305 {
306 return empty_vconfig();
307 }
308 }
309 }
310 return unconstructed_vconfig();
311}
312
313/**
314 * Returns whether or not *this has a child whose key is @a key.
315 */
316bool vconfig::has_child(const std::string& key) const
317{
318 if (cfg_->has_child(key)) {
319 return true;
320 }
321 for (const config &ins : cfg_->child_range("insert_tag"))
322 {
323 vconfig insert_cfg(ins, *variables_);
324 if(insert_cfg["name"] == key) {
325 return true;
326 }
327 }
328 return false;
329}
330
331namespace {
332 struct vconfig_expand_visitor
333#ifdef USING_BOOST_VARIANT
334 : boost::static_visitor<void>
335#endif
336 {
338 const variable_set& vars;
339
340 vconfig_expand_visitor(config::attribute_value &r, const variable_set& vars): result(r), vars(vars) {}
341 template<typename T> void operator()(const T&) const {}
342 void operator()(const std::string &s) const
343 {
345 }
346 void operator()(const t_string &s) const
347 {
349 }
350 };
351}//unnamed namespace
352
353config::attribute_value vconfig::expand(const std::string &key) const
354{
355 config::attribute_value val = (*cfg_)[key];
356 val.apply_visitor(vconfig_expand_visitor(val, *variables_));
357 return val;
358}
359
361{
362 config::attribute val = *i_;
363 val.second.apply_visitor(vconfig_expand_visitor(val.second, *variables_));
364 return val;
365}
366
368{
369 config::attribute val = *i_;
370 val.second.apply_visitor(vconfig_expand_visitor(val.second, *variables_));
371 pointer_proxy p {val};
372 return p;
373}
374
376 i_(i), inner_index_(0), cache_(), variables_(&vars)
377{
378}
379
380vconfig::all_children_iterator::all_children_iterator(const Itor &i, const variable_set& vars, const std::shared_ptr<const config> & cache) :
381 i_(i), inner_index_(0), cache_(cache), variables_(&vars)
382{
383}
384
386{
387 if (inner_index_ >= 0 && i_->key == "insert_tag")
388 {
389 try
390 {
392
393 config::const_child_itors range = vinfo.as_array();
394
395 if (++inner_index_ < static_cast<int>(range.size()))
396 {
397 return *this;
398 }
399
400 }
402 {
403 }
404 inner_index_ = 0;
405 }
406 ++i_;
407 return *this;
408}
409
416
418{
419 if(inner_index_ >= 0 && i_->key == "insert_tag") {
420 if(--inner_index_ >= 0) {
421 return *this;
422 }
423 inner_index_ = 0;
424 }
425 --i_;
426 return *this;
427}
428
435
440
446
447
449{
450 const std::string &key = i_->key;
451 if (inner_index_ >= 0 && key == "insert_tag") {
452 return vconfig(i_->cfg, *variables_)["name"];
453 }
454 return key;
455}
456
458{
459 if (inner_index_ >= 0 && i_->key == "insert_tag")
460 {
461 try
462 {
463 config::const_child_itors range = as_nonempty_range(vconfig(i_->cfg, *variables_)["variable"], *variables_);
464
465 range.advance_begin(inner_index_);
466 return vconfig(range.front(), true, variables_);
467 }
469 {
470 return empty_vconfig();
471 }
472 }
473 return vconfig(i_->cfg, cache_, *variables_);
474}
475
477{
478 return i_ == i.i_ && inner_index_ == i.inner_index_;
479}
480
485
490
491scoped_wml_variable::scoped_wml_variable(const std::string& var_name) :
492 previous_val_(),
493 var_name_(var_name),
494 activated_(false)
495{
497 resources::gamedata->scoped_variables.push_back(this);
498}
499
501{
502 try
503 {
506 }
509 LOG_NG << "scoped_wml_variable: var_name \"" << var_name_ << "\" has been auto-stored.";
510 activated_ = true;
511 return res;
512 }
514 {
515 assert(false && "invalid variable name of autostored variable");
516 throw "assertion ignored";
517 }
518
519}
520
522{
524 return;
525 }
526
527 if(activated_) {
530 {
531 try
532 {
534 }
536 {
537 }
538 }
539 LOG_NG << "scoped_wml_variable: var_name \"" << var_name_ << "\" has been reverted.";
540 }
541
542 assert(resources::gamedata->scoped_variables.back() == this);
544}
545
547{
549 if(itor != umap_.end()) {
550 config &tmp_cfg = store();
551 itor->write(tmp_cfg);
552 tmp_cfg["x"] = loc_.wml_x();
553 tmp_cfg["y"] = loc_.wml_y();
554 LOG_NG << "auto-storing $" << name() << " at (" << loc_ << ")";
555 } else {
556 ERR_NG << "failed to auto-store $" << name() << " at (" << loc_ << ")";
557 }
558}
559
561{
562 if (data_) {
563 store(*data_);
564 }
565}
566
568{
569 assert(resources::gameboard);
570
571 const std::vector<team>& teams = resources::gameboard->teams();
572 auto team_it = utils::ranges::find(teams, player_, &team::save_id_or_number);
573
574 if(team_it != teams.end()) {
575 if(team_it->recall_list().size() > recall_index_) {
576 config &tmp_cfg = store();
577 team_it->recall_list()[recall_index_]->write(tmp_cfg);
578 tmp_cfg["x"] = "recall";
579 tmp_cfg["y"] = "recall";
580 LOG_NG << "auto-storing $" << name() << " for player: " << player_
581 << " at recall index: " << recall_index_;
582 } else {
583 ERR_NG << "failed to auto-store $" << name() << " for player: " << player_
584 << " at recall index: " << recall_index_;
585 }
586 } else {
587 ERR_NG << "failed to auto-store $" << name() << " for player: " << player_;
588 }
589}
#define WRN_NG
#define ERR_NG
#define LOG_NG
Variant for storing WML attributes.
auto apply_visitor(const V &visitor) const
Visitor support: Applies a visitor to the underlying variant.
virtual config::attribute_value get_variable_const(const std::string &id) const
Definition variable.cpp:66
const config & cfg_
Definition variable.hpp:28
virtual variable_access_const get_variable_access_read(const std::string &varname) const
Definition variable.cpp:76
A config object defines a single node in a WML file, with access to child nodes.
Definition config.hpp:157
config & add_child(std::string_view key)
Definition config.cpp:436
all_children_iterator erase(const all_children_iterator &i)
Definition config.cpp:618
const_all_children_iterator ordered_begin() const
Definition config.cpp:838
optional_config_impl< config > optional_child(std::string_view key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
Definition config.cpp:380
const_attr_itors attribute_range() const
Definition config.cpp:740
config_attribute_value attribute_value
Variant for storing WML attributes.
Definition config.hpp:290
auto all_children_view() const
In-order iteration over all children.
Definition config.hpp:795
const_all_children_iterator ordered_end() const
Definition config.cpp:848
child_itors child_range(std::string_view key)
Definition config.cpp:268
const attribute_value * get(std::string_view key) const
Returns a pointer to the attribute with the given key or nullptr if it does not exist.
Definition config.cpp:665
attribute_map::value_type attribute
Definition config.hpp:297
bool has_child(std::string_view key) const
Determine whether a config has a child or not.
Definition config.cpp:312
boost::iterator_range< const_child_iterator > const_child_itors
Definition config.hpp:281
virtual const std::vector< team > & teams() const override
std::vector< scoped_wml_variable * > scoped_variables
Definition game_data.hpp:31
void clear_variable_cfg(const std::string &varname)
Clears only the config children does nothing if varname is no valid variable name.
const config & get_variables() const
Definition game_data.hpp:33
config & add_variable_cfg(const std::string &varname, const config &value=config())
throws invalid_variablename_exception if varname is no valid variable name.
unsigned int recall_index_
Definition variable.hpp:269
const std::string player_
Definition variable.hpp:268
optional_const_config data_
Definition variable.hpp:246
scoped_wml_variable(const std::string &var_name)
Definition variable.cpp:491
const std::string & name() const
Definition variable.hpp:229
config & store(const config &var_value=config())
Definition variable.cpp:500
virtual ~scoped_wml_variable()
Definition variable.cpp:521
const std::string var_name_
Definition variable.hpp:235
const map_location loc_
Definition variable.hpp:256
const unit_map & umap_
Definition variable.hpp:257
std::string save_id_or_number() const
Definition team.hpp:259
unit_iterator end()
Definition map.hpp:428
unit_iterator find(std::size_t id)
Definition map.cpp:302
Information on a WML variable.
maybe_const_t< config::child_itors, V > as_array() const
If instantiated with vi_policy_const, the lifetime of the returned const attribute_value reference mi...
maybe_const_t< config::attribute_value, V > & as_scalar() const
If instantiated with vi_policy_const, the lifetime of the returned const attribute_value reference mi...
virtual config::attribute_value get_variable_const(const std::string &id) const =0
virtual variable_access_const get_variable_access_read(const std::string &varname) const =0
A variable-expanding proxy for the config class.
Definition variable.hpp:45
~vconfig()
Default destructor, but defined here for possibly faster compiles (templates sometimes can be rough o...
Definition variable.cpp:138
bool memory_managed() const
Returns true if *this has made a copy of its config.
Definition variable.hpp:196
all_children_iterator ordered_begin() const
In-order iteration over all children.
Definition variable.cpp:481
std::vector< vconfig > child_list
Definition variable.hpp:78
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
static vconfig empty_vconfig()
Definition variable.cpp:142
vconfig child(const std::string &key) const
Returns a child of *this whose key is key.
Definition variable.cpp:289
config::attribute_value expand(const std::string &) const
Definition variable.cpp:353
config get_parsed_config() const
Definition variable.cpp:177
all_children_iterator ordered_end() const
Definition variable.cpp:486
const vconfig & make_safe() const
instruct the vconfig to make a private copy of its underlying data.
Definition variable.cpp:164
std::shared_ptr< const config > cache_
Keeps a copy of our config alive when we manage our own memory.
Definition variable.hpp:202
static const config default_empty_config
Definition variable.hpp:206
const config * cfg_
Used to access our config (original or copy, as appropriate).
Definition variable.hpp:204
const config & get_config() const
Definition variable.hpp:75
std::size_t count_children(const std::string &key) const
Definition variable.cpp:257
child_list get_children(const std::string &key) const
Definition variable.cpp:227
const variable_set * variables_
Definition variable.hpp:205
bool has_child(const std::string &key) const
Returns whether or not *this has a child whose key is key.
Definition variable.cpp:316
const config * cfg
static std::string _(const char *str)
Definition gettext.hpp:100
std::string id
Text to match against addon_info.tags()
Definition manager.cpp:199
Standard logging facilities (interface).
game_board * gameboard
Definition resources.cpp:20
game_data * gamedata
Definition resources.cpp:22
STL namespace.
auto find(Container &container, const Value &value, const Projection &projection={})
Definition general.hpp:213
std::string interpolate_variables_into_string(const std::string &str, const string_map *const symbols)
Function which will interpolate variables, starting with '$' in the string 'str' with the equivalent ...
t_string interpolate_variables_into_tstring(const t_string &tstr, const variable_set &variables)
Function that does the same as the above, for t_stringS.
std::unordered_map< std::string, cache_item > cache
Definition sound.cpp:146
int wml_y() const
Definition location.hpp:187
int wml_x() const
Definition location.hpp:186
all_children_iterator(const Itor &i, const variable_set &vars)
Definition variable.cpp:375
all_children_iterator & operator++()
Definition variable.cpp:385
const std::pair< std::string, vconfig > value_type
Definition variable.hpp:140
reference operator*() const
Definition variable.cpp:436
bool operator==(const all_children_iterator &i) const
Definition variable.cpp:476
std::string get_key() const
Definition variable.cpp:448
all_children_iterator & operator--()
Definition variable.cpp:417
pointer operator->() const
Definition variable.cpp:367
reference operator*() const
Definition variable.cpp:360
const config::attribute reference
Definition variable.hpp:110
const variable_set * variables_
Definition variable.hpp:128
mock_party p
static map_location::direction n
static map_location::direction s
static lg::log_domain log_engine("engine")
static const variable_set * try_get_gamedata()
Definition variable.cpp:82
variable_info< const variable_info_implementation::vi_policy_const > variable_access_const
Read-only access.
#define i