The Battle for Wesnoth  1.17.14+dev
variable.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2005 - 2022
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 
24 #include "formula/string_utils.hpp"
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 "team.hpp"
32 
33 static lg::log_domain log_engine("engine");
34 #define LOG_NG LOG_STREAM(info, log_engine)
35 #define WRN_NG LOG_STREAM(warn, log_engine)
36 #define ERR_NG LOG_STREAM(err, log_engine)
37 namespace
38 {
39  const config as_nonempty_range_default("_");
40  config::const_child_itors as_nonempty_range(const std::string& varname, const variable_set& vars)
41  {
43 
44  if(!range.empty()) {
45  return range;
46  }
47 
48  return as_nonempty_range_default.child_range("_");
49  }
50 
51  // doxygen didn't like this as an anonymous struct
52  struct anon : public variable_set
53  {
54  config::attribute_value get_variable_const(const std::string&) const override
55  {
56  return config::attribute_value();
57  }
58  variable_access_const get_variable_access_read(const std::string& varname) const override
59  {
60  return variable_access_const(varname, config());
61  }
62  } null_variable_set;
63 }
64 
66  try {
68  return variable.as_scalar();
69  } catch(const invalid_variablename_exception&) {
70  ERR_NG << "invalid variablename " << id;
71  return config::attribute_value();
72  }
73 }
74 
76  return variable_access_const(id, cfg_);
77 }
78 
80 
82 {
84  return resources::gamedata;
85  }
86  return &null_variable_set;
87 }
88 
90  cache_(), cfg_(&default_empty_config), variables_(try_get_gamedata())
91 {
92 }
93 
94 vconfig::vconfig(const config & cfg, const std::shared_ptr<const config> & cache) :
95  cache_(cache), cfg_(&cfg), variables_(try_get_gamedata())
96 {
97 }
98 
99 /**
100  * Constructor from a config, with an option to manage memory.
101  * @param[in] cfg The "WML source" of the vconfig being constructed.
102  * @param[in] manage_memory If true, a copy of @a cfg will be made, allowing the
103  * vconfig to safely persist after @a cfg is destroyed.
104  * If false, no copy is made, so @a cfg must be
105  * guaranteed to persist as long as the vconfig will.
106  * If in doubt, set to true; it is less efficient, but safe.
107  * @param[in] vars
108  * See also make_safe().
109  */
110 vconfig::vconfig(const config &cfg, bool manage_memory, const variable_set* vars)
111  : cache_(manage_memory ? new config(cfg) : nullptr)
112  , cfg_(manage_memory ? cache_.get() : &cfg)
113  , variables_(vars ? vars : try_get_gamedata())
114 {
115 }
116 
118  : cache_(), cfg_(&cfg), variables_(try_get_gamedata())
119 {}
120 
122  : cache_(new config(std::move(cfg))), cfg_(cache_.get()), variables_(try_get_gamedata())
123 {}
124 
125 vconfig::vconfig(const config& cfg, const std::shared_ptr<const config> & cache, const variable_set& variables)
126  : cache_(cache), cfg_(&cfg), variables_(&variables)
127 {}
128 
129 vconfig::vconfig(const config& cfg, const variable_set& variables)
130  : cache_(), cfg_(&cfg), variables_(&variables)
131 {}
132 
133 /**
134  * Default destructor, but defined here for possibly faster compiles
135  * (templates sometimes can be rough on the compiler).
136  */
138 {
139 }
140 
142 {
143  static const config empty_config;
144  return vconfig(empty_config, false);
145 }
146 
147 /**
148  * This is just a wrapper for the default constructor; it exists for historical
149  * reasons and to make it clear that default construction cannot be dereferenced
150  * (in contrast to an empty vconfig).
151  */
153 {
154  return vconfig();
155 }
156 
157 /**
158  * Ensures that *this manages its own memory, making it safe for *this to
159  * outlive the config it was ultimately constructed from.
160  * It is perfectly safe to call this for a vconfig that already manages its memory.
161  * This does not work on a null() vconfig.
162  */
164 {
165  // Nothing to do if we already manage our own memory.
166  if ( memory_managed() )
167  return *this;
168 
169  // Make a copy of our config.
170  cache_.reset(new config(*cfg_));
171  // Use our copy instead of the original.
172  cfg_ = cache_.get();
173  return *this;
174 }
175 
177 {
178  // Keeps track of insert_tag variables.
179  static std::set<std::string> vconfig_recursion;
180 
181  config res;
182 
183  for (const config::attribute &i : cfg_->attribute_range()) {
184  res[i.first] = expand(i.first);
185  }
186 
188  {
189  if (child.key == "insert_tag") {
190  vconfig insert_cfg(child.cfg, *variables_);
191  std::string name = insert_cfg["name"];
192  std::string vname = insert_cfg["variable"];
193  if(!vconfig_recursion.insert(vname).second) {
194  throw recursion_error("vconfig::get_parsed_config() infinite recursion detected, aborting");
195  }
196  try
197  {
198  config::const_child_itors range = as_nonempty_range(vname, *variables_);
199  for (const config& ch : range)
200  {
201  res.add_child(name, vconfig(ch, *variables_).get_parsed_config());
202  }
203  }
204  catch(const invalid_variablename_exception&)
205  {
206  res.add_child(name);
207  }
208  catch(const recursion_error &err) {
209  vconfig_recursion.erase(vname);
210  WRN_NG << err.message;
211  if(vconfig_recursion.empty()) {
212  res.add_child("insert_tag", insert_cfg.get_config());
213  } else {
214  // throw to the top [insert_tag] which started the recursion
215  throw;
216  }
217  }
218  vconfig_recursion.erase(vname);
219  } else {
221  }
222  }
223  return res;
224 }
225 
226 vconfig::child_list vconfig::get_children(const std::string& key) const
227 {
229 
231  {
232  if (child.key == key) {
233  res.push_back(vconfig(child.cfg, cache_, *variables_));
234  } else if (child.key == "insert_tag") {
235  vconfig insert_cfg(child.cfg, *variables_);
236  if(insert_cfg["name"] == key)
237  {
238  try
239  {
240  config::const_child_itors range = as_nonempty_range(insert_cfg["variable"], *variables_);
241  for (const config& ch : range)
242  {
243  res.push_back(vconfig(ch, true, variables_));
244  }
245  }
246  catch(const invalid_variablename_exception&)
247  {
248  res.push_back(empty_vconfig());
249  }
250  }
251  }
252  }
253  return res;
254 }
255 
256 std::size_t vconfig::count_children(const std::string& key) const
257 {
258  std::size_t n = 0;
259 
261  {
262  if (child.key == key) {
263  n++;
264  } else if (child.key == "insert_tag") {
265  vconfig insert_cfg(child.cfg, *variables_);
266  if(insert_cfg["name"] == key)
267  {
268  try
269  {
270  config::const_child_itors range = as_nonempty_range(insert_cfg["variable"], *variables_);
271  n += range.size();
272  }
273  catch(const invalid_variablename_exception&)
274  {
275  n++;
276  }
277  }
278  }
279  }
280  return n;
281 }
282 
283 /**
284  * Returns a child of *this whose key is @a key.
285  * If no such child exists, returns an unconstructed vconfig (use null() to test
286  * for this).
287  */
288 vconfig vconfig::child(const std::string& key) const
289 {
290  if (const config &natural = cfg_->child(key)) {
291  return vconfig(natural, cache_, *variables_);
292  }
293  for (const config &ins : cfg_->child_range("insert_tag"))
294  {
295  vconfig insert_cfg(ins, *variables_);
296  if(insert_cfg["name"] == key)
297  {
298  try
299  {
300  config::const_child_itors range = as_nonempty_range(insert_cfg["variable"], *variables_);
301  return vconfig(range.front(), true, variables_);
302  }
303  catch(const invalid_variablename_exception&)
304  {
305  return empty_vconfig();
306  }
307  }
308  }
309  return unconstructed_vconfig();
310 }
311 
312 /**
313  * Returns whether or not *this has a child whose key is @a key.
314  */
315 bool vconfig::has_child(const std::string& key) const
316 {
317  if (cfg_->child(key)) {
318  return true;
319  }
320  for (const config &ins : cfg_->child_range("insert_tag"))
321  {
322  vconfig insert_cfg(ins, *variables_);
323  if(insert_cfg["name"] == key) {
324  return true;
325  }
326  }
327  return false;
328 }
329 
330 namespace {
331  struct vconfig_expand_visitor
332 #ifdef USING_BOOST_VARIANT
333  : boost::static_visitor<void>
334 #endif
335  {
336  config::attribute_value &result;
337  const variable_set& vars;
338 
339  vconfig_expand_visitor(config::attribute_value &r, const variable_set& vars): result(r), vars(vars) {}
340  template<typename T> void operator()(const T&) const {}
341  void operator()(const std::string &s) const
342  {
344  }
345  void operator()(const t_string &s) const
346  {
348  }
349  };
350 }//unnamed namespace
351 
352 config::attribute_value vconfig::expand(const std::string &key) const
353 {
354  config::attribute_value val = (*cfg_)[key];
355  val.apply_visitor(vconfig_expand_visitor(val, *variables_));
356  return val;
357 }
358 
360 {
361  config::attribute val = *i_;
362  val.second.apply_visitor(vconfig_expand_visitor(val.second, *variables_));
363  return val;
364 }
365 
367 {
368  config::attribute val = *i_;
369  val.second.apply_visitor(vconfig_expand_visitor(val.second, *variables_));
370  pointer_proxy p {val};
371  return p;
372 }
373 
375  i_(i), inner_index_(0), cache_(), variables_(&vars)
376 {
377 }
378 
379 vconfig::all_children_iterator::all_children_iterator(const Itor &i, const variable_set& vars, const std::shared_ptr<const config> & cache) :
380  i_(i), inner_index_(0), cache_(cache), variables_(&vars)
381 {
382 }
383 
385 {
386  if (inner_index_ >= 0 && i_->key == "insert_tag")
387  {
388  try
389  {
391 
392  config::const_child_itors range = vinfo.as_array();
393 
394  if (++inner_index_ < static_cast<int>(range.size()))
395  {
396  return *this;
397  }
398 
399  }
400  catch(const invalid_variablename_exception&)
401  {
402  }
403  inner_index_ = 0;
404  }
405  ++i_;
406  return *this;
407 }
408 
410 {
412  this->operator++();
413  return i;
414 }
415 
417 {
418  if(inner_index_ >= 0 && i_->key == "insert_tag") {
419  if(--inner_index_ >= 0) {
420  return *this;
421  }
422  inner_index_ = 0;
423  }
424  --i_;
425  return *this;
426 }
427 
429 {
431  this->operator--();
432  return i;
433 }
434 
436 {
437  return value_type(get_key(), get_child());
438 }
439 
441 {
443  return p;
444 }
445 
446 
448 {
449  const std::string &key = i_->key;
450  if (inner_index_ >= 0 && key == "insert_tag") {
451  return vconfig(i_->cfg, *variables_)["name"];
452  }
453  return key;
454 }
455 
457 {
458  if (inner_index_ >= 0 && i_->key == "insert_tag")
459  {
460  try
461  {
462  config::const_child_itors range = as_nonempty_range(vconfig(i_->cfg, *variables_)["variable"], *variables_);
463 
464  range.advance_begin(inner_index_);
465  return vconfig(range.front(), true, variables_);
466  }
467  catch(const invalid_variablename_exception&)
468  {
469  return empty_vconfig();
470  }
471  }
472  return vconfig(i_->cfg, cache_, *variables_);
473 }
474 
476 {
477  return i_ == i.i_ && inner_index_ == i.inner_index_;
478 }
479 
481 {
483 }
484 
486 {
488 }
489 
490 scoped_wml_variable::scoped_wml_variable(const std::string& var_name) :
491  previous_val_(),
492  var_name_(var_name),
493  activated_(false)
494 {
496  resources::gamedata->scoped_variables.push_back(this);
497 }
498 
500 {
501  try
502  {
503  for (const config &i : resources::gamedata->get_variables().child_range(var_name_)) {
505  }
508  LOG_NG << "scoped_wml_variable: var_name \"" << var_name_ << "\" has been auto-stored.";
509  activated_ = true;
510  return res;
511  }
512  catch(const invalid_variablename_exception&)
513  {
514  assert(false && "invalid variable name of autostored variable");
515  throw "assertion ignored";
516  }
517 
518 }
519 
521 {
522  if(!resources::gamedata) {
523  return;
524  }
525 
526  if(activated_) {
529  {
530  try
531  {
533  }
534  catch(const invalid_variablename_exception&)
535  {
536  }
537  }
538  LOG_NG << "scoped_wml_variable: var_name \"" << var_name_ << "\" has been reverted.";
539  }
540 
541  assert(resources::gamedata->scoped_variables.back() == this);
543 }
544 
546 {
547  unit_map::const_iterator itor = umap_.find(loc_);
548  if(itor != umap_.end()) {
549  config &tmp_cfg = store();
550  itor->write(tmp_cfg);
551  tmp_cfg["x"] = loc_.wml_x();
552  tmp_cfg["y"] = loc_.wml_y();
553  LOG_NG << "auto-storing $" << name() << " at (" << loc_ << ")";
554  } else {
555  ERR_NG << "failed to auto-store $" << name() << " at (" << loc_ << ")";
556  }
557 }
558 
560 {
561  if (data_) {
562  store(data_);
563  }
564 }
565 
567 {
568  assert(resources::gameboard);
569 
570  const std::vector<team>& teams = resources::gameboard->teams();
571 
572  std::vector<team>::const_iterator team_it = std::find_if(teams.begin(), teams.end(), [&](const team& t) { return t.save_id_or_number() == player_; });
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 }
void activate()
Definition: variable.cpp:545
virtual variable_access_const get_variable_access_read(const std::string &varname) const =0
virtual config::attribute_value get_variable_const(const std::string &id) const
Definition: variable.cpp:65
config & child(config_key_type key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
Definition: config.cpp:402
vconfig()
Definition: variable.cpp:89
const_all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:978
std::string interpolate_variables_into_string(const std::string &str, const string_map *const symbols)
virtual const std::vector< team > & teams() const override
Definition: game_board.hpp:86
vconfig child(const std::string &key) const
Returns a child of *this whose key is key.
Definition: variable.cpp:288
std::shared_ptr< const config > cache_
Definition: variable.hpp:179
reference operator*() const
Definition: variable.cpp:359
all_children_iterator ordered_begin() const
In-order iteration over all children.
Definition: variable.cpp:480
Variant for storing WML attributes.
const variable_set * variables_
Definition: variable.hpp:180
all_children_iterator & operator--()
Definition: variable.cpp:416
virtual variable_access_const get_variable_access_read(const std::string &varname) const
Definition: variable.cpp:75
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...
child_itors child_range(config_key_type key)
Definition: config.cpp:344
attribute_map::value_type attribute
Definition: config.hpp:221
int wml_x() const
Definition: location.hpp:153
~vconfig()
Default destructor, but defined here for possibly faster compiles (templates sometimes can be rough o...
Definition: variable.cpp:137
#define LOG_NG
Definition: variable.cpp:34
STL namespace.
const config::attribute reference
Definition: variable.hpp:110
std::string get_key() const
Definition: variable.cpp:447
static lg::log_domain log_engine("engine")
scoped_wml_variable(const std::string &var_name)
Definition: variable.cpp:490
const_attr_itors attribute_range() const
Definition: config.cpp:858
game_data * gamedata
Definition: resources.cpp:23
map_location loc_
const config * cfg_
Used to access our config (original or copy, as appropriate).
Definition: variable.hpp:204
variable_info< const variable_info_implementation::vi_policy_const > variable_access_const
Read-only access.
const_all_children_iterator ordered_end() const
Definition: config.cpp:968
child_list get_children(const std::string &key) const
Definition: variable.cpp:226
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:75
const vconfig & make_safe() const
instruct the vconfig to make a private copy of its underlying data.
Definition: variable.cpp:163
#define WRN_NG
Definition: variable.cpp:35
const std::string & name() const
Definition: variable.hpp:229
int wml_y() const
Definition: location.hpp:154
all_children_iterator ordered_end() const
Definition: variable.cpp:485
bool memory_managed() const
Returns true if *this has made a copy of its config.
Definition: variable.hpp:196
game_board * gameboard
Definition: resources.cpp:21
bool operator==(const all_children_iterator &i) const
Definition: variable.cpp:475
config get_parsed_config() const
Definition: variable.cpp:176
std::shared_ptr< const config > cache_
Keeps a copy of our config alive when we manage our own memory.
Definition: variable.hpp:202
std::size_t count_children(const std::string &key) const
Definition: variable.cpp:256
boost::iterator_range< const_child_iterator > const_child_itors
Definition: config.hpp:205
config & store(const config &var_value=config())
Definition: variable.cpp:499
std::string id
Text to match against addon_info.tags()
Definition: manager.cpp:215
std::vector< scoped_wml_variable * > scoped_variables
Definition: game_data.hpp:32
std::size_t i
Definition: function.cpp:968
logger & err()
Definition: log.cpp:216
config::attribute_value expand(const std::string &) const
Definition: variable.cpp:352
mock_party p
bool has_child(const std::string &key) const
Returns whether or not *this has a child whose key is key.
Definition: variable.cpp:315
static const variable_set * try_get_gamedata()
Definition: variable.cpp:81
static map_location::DIRECTION s
void clear_variable_cfg(const std::string &varname)
Clears only the config children does nothing if varname is no valid variable name.
Definition: game_data.cpp:103
CURSOR_TYPE get()
Definition: cursor.cpp:216
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:152
config & add_child(config_key_type key)
Definition: config.cpp:514
#define ERR_NG
Definition: variable.cpp:36
const_all_children_iterator ordered_begin() const
Definition: config.cpp:958
virtual config::attribute_value get_variable_const(const std::string &id) const =0
Information on a WML variable.
double t
Definition: astarsearch.cpp:65
const std::pair< std::string, vconfig > value_type
Definition: variable.hpp:138
A variable-expanding proxy for the config class.
Definition: variable.hpp:44
const config & get_config() const
Definition: variable.hpp:75
Standard logging facilities (interface).
auto apply_visitor(const V &visitor) const
Visitor support: Applies a visitor to the underlying variant.
std::string message
Definition: exceptions.hpp:30
static vconfig empty_vconfig()
Definition: variable.cpp:141
const variable_set * variables_
Definition: variable.hpp:205
config & add_variable_cfg(const std::string &varname, const config &value=config())
throws invalid_variablename_exception if varname is no valid variable name.
Definition: game_data.cpp:97
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...
static const config default_empty_config
Definition: variable.hpp:206
std::vector< vconfig > child_list
Definition: variable.hpp:78
pointer operator->() const
Definition: variable.cpp:366
config_attribute_value attribute_value
Variant for storing WML attributes.
Definition: config.hpp:214
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:60
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.
const std::string var_name_
Definition: variable.hpp:235
static map_location::DIRECTION n
virtual ~scoped_wml_variable()
Definition: variable.cpp:520
reference operator*() const
Definition: variable.cpp:435
const value_type reference
Definition: variable.hpp:144
all_children_iterator(const Itor &i, const variable_set &vars)
Definition: variable.cpp:374
all_children_iterator & operator++()
Definition: variable.cpp:384