The Battle for Wesnoth  1.15.2+dev
variable.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 by David White <dave@whitevine.net>
3  Copyright (C) 2005 - 2018 by Philippe Plantier <ayin@anathas.org>
4 
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 #include <boost/variant/static_visitor.hpp>
34 
35 static lg::log_domain log_engine("engine");
36 #define LOG_NG LOG_STREAM(info, log_engine)
37 #define WRN_NG LOG_STREAM(warn, log_engine)
38 #define ERR_NG LOG_STREAM(err, log_engine)
39 namespace
40 {
41  const config as_nonempty_range_default("_");
42  config::const_child_itors as_nonempty_range(const std::string& varname, const variable_set& vars)
43  {
45 
46  if(!range.empty()) {
47  return range;
48  }
49 
50  return as_nonempty_range_default.child_range("_");
51  }
52 
53  struct : public variable_set
54  {
55  config::attribute_value get_variable_const(const std::string&) const override
56  {
57  return config::attribute_value();
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 << "\n";
72  return config::attribute_value();
73  }
74 }
75 
77  return variable_access_const(id, cfg_);
78 }
79 
81 
83 {
85  return resources::gamedata;
86  }
87  return &null_variable_set;
88 }
89 
91  cache_(), cfg_(&default_empty_config), variables_(try_get_gamedata())
92 {
93 }
94 
95 vconfig::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  * 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 << std::endl;
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 : boost::static_visitor<void>
332  {
333  config::attribute_value &result;
334  const variable_set& vars;
335 
336  vconfig_expand_visitor(config::attribute_value &r, const variable_set& vars): result(r), vars(vars) {}
337  template<typename T> void operator()(const T&) const {}
338  void operator()(const std::string &s) const
339  {
341  }
342  void operator()(const t_string &s) const
343  {
345  }
346  };
347 }//unnamed namespace
348 
349 config::attribute_value vconfig::expand(const std::string &key) const
350 {
351  config::attribute_value val = (*cfg_)[key];
352  val.apply_visitor(vconfig_expand_visitor(val, *variables_));
353  return val;
354 }
355 
357 {
358  config::attribute val = *i_;
359  val.second.apply_visitor(vconfig_expand_visitor(val.second, *variables_));
360  return val;
361 }
362 
364 {
365  config::attribute val = *i_;
366  val.second.apply_visitor(vconfig_expand_visitor(val.second, *variables_));
367  pointer_proxy p {val};
368  return p;
369 }
370 
372  i_(i), inner_index_(0), cache_(), variables_(&vars)
373 {
374 }
375 
376 vconfig::all_children_iterator::all_children_iterator(const Itor &i, const variable_set& vars, const std::shared_ptr<const config> & cache) :
377  i_(i), inner_index_(0), cache_(cache), variables_(&vars)
378 {
379 }
380 
382 {
383  if (inner_index_ >= 0 && i_->key == "insert_tag")
384  {
385  try
386  {
388 
389  config::const_child_itors range = vinfo.as_array();
390 
391  if (++inner_index_ < static_cast<int>(range.size()))
392  {
393  return *this;
394  }
395 
396  }
397  catch(const invalid_variablename_exception&)
398  {
399  }
400  inner_index_ = 0;
401  }
402  ++i_;
403  return *this;
404 }
405 
407 {
409  this->operator++();
410  return i;
411 }
412 
414 {
415  if(inner_index_ >= 0 && i_->key == "insert_tag") {
416  if(--inner_index_ >= 0) {
417  return *this;
418  }
419  inner_index_ = 0;
420  }
421  --i_;
422  return *this;
423 }
424 
426 {
428  this->operator--();
429  return i;
430 }
431 
433 {
434  return value_type(get_key(), get_child());
435 }
436 
438 {
440  return p;
441 }
442 
443 
445 {
446  const std::string &key = i_->key;
447  if (inner_index_ >= 0 && key == "insert_tag") {
448  return vconfig(i_->cfg, *variables_)["name"];
449  }
450  return key;
451 }
452 
454 {
455  if (inner_index_ >= 0 && i_->key == "insert_tag")
456  {
457  try
458  {
459  config::const_child_itors range = as_nonempty_range(vconfig(i_->cfg, *variables_)["variable"], *variables_);
460 
461  range.advance_begin(inner_index_);
462  return vconfig(range.front(), true, variables_);
463  }
464  catch(const invalid_variablename_exception&)
465  {
466  return empty_vconfig();
467  }
468  }
469  return vconfig(i_->cfg, cache_, *variables_);
470 }
471 
473 {
474  return i_ == i.i_ && inner_index_ == i.inner_index_;
475 }
476 
478 {
480 }
481 
483 {
485 }
486 
487 scoped_wml_variable::scoped_wml_variable(const std::string& var_name) :
488  previous_val_(),
489  var_name_(var_name),
490  activated_(false)
491 {
493  resources::gamedata->scoped_variables.push_back(this);
494 }
495 
497 {
498  try
499  {
500  for (const config &i : resources::gamedata->get_variables().child_range(var_name_)) {
502  }
505  LOG_NG << "scoped_wml_variable: var_name \"" << var_name_ << "\" has been auto-stored.\n";
506  activated_ = true;
507  return res;
508  }
509  catch(const invalid_variablename_exception&)
510  {
511  assert(false && "invalid variable name of autostored variable");
512  throw "assertion ignored";
513  }
514 
515 }
516 
518 {
519  if(!resources::gamedata) {
520  return;
521  }
522 
523  if(activated_) {
526  {
527  try
528  {
530  }
531  catch(const invalid_variablename_exception&)
532  {
533  }
534  }
535  LOG_NG << "scoped_wml_variable: var_name \"" << var_name_ << "\" has been reverted.\n";
536  }
537 
538  assert(resources::gamedata->scoped_variables.back() == this);
540 }
541 
543 {
544  unit_map::const_iterator itor = umap_.find(loc_);
545  if(itor != umap_.end()) {
546  config &tmp_cfg = store();
547  itor->write(tmp_cfg);
548  tmp_cfg["x"] = loc_.wml_x();
549  tmp_cfg["y"] = loc_.wml_y();
550  LOG_NG << "auto-storing $" << name() << " at (" << loc_ << ")\n";
551  } else {
552  ERR_NG << "failed to auto-store $" << name() << " at (" << loc_ << ")" << std::endl;
553  }
554 }
555 
557 {
558  if (data_) {
559  store(data_);
560  }
561 }
562 
564 {
565  assert(resources::gameboard);
566 
567  const std::vector<team>& teams = resources::gameboard->teams();
568 
569  std::vector<team>::const_iterator team_it = std::find_if(teams.begin(), teams.end(), [&](const team& t) { return t.save_id_or_number() == player_; });
570 
571  if(team_it != teams.end()) {
572  if(team_it->recall_list().size() > recall_index_) {
573  config &tmp_cfg = store();
574  team_it->recall_list()[recall_index_]->write(tmp_cfg);
575  tmp_cfg["x"] = "recall";
576  tmp_cfg["y"] = "recall";
577  LOG_NG << "auto-storing $" << name() << " for player: " << player_
578  << " at recall index: " << recall_index_ << '\n';
579  } else {
580  ERR_NG << "failed to auto-store $" << name() << " for player: " << player_
581  << " at recall index: " << recall_index_ << '\n';
582  }
583  } else {
584  ERR_NG << "failed to auto-store $" << name() << " for player: " << player_ << '\n';
585  }
586 }
void activate()
Definition: variable.cpp:542
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:66
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:420
vconfig()
Definition: variable.cpp:90
const_all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:921
std::string interpolate_variables_into_string(const std::string &str, const string_map *const symbols)
Function which will interpolate variables, starting with &#39;$&#39; in the string &#39;str&#39; with the equivalent ...
virtual const std::vector< team > & teams() const override
Definition: game_board.hpp:92
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:186
reference operator*() const
Definition: variable.cpp:356
all_children_iterator ordered_begin() const
In-order iteration over all children.
Definition: variable.cpp:477
Variant for storing WML attributes.
const variable_set * variables_
Definition: variable.hpp:187
all_children_iterator & operator--()
Definition: variable.cpp:413
virtual variable_access_const get_variable_access_read(const std::string &varname) const
Definition: variable.cpp:76
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:362
attribute_map::value_type attribute
Definition: config.hpp:226
int wml_x() const
Definition: location.hpp:157
~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:36
STL namespace.
const config::attribute reference
Definition: variable.hpp:117
std::string get_key() const
Definition: variable.cpp:444
-file sdl_utils.hpp
static lg::log_domain log_engine("engine")
scoped_wml_variable(const std::string &var_name)
Definition: variable.cpp:487
const_attr_itors attribute_range() const
Definition: config.cpp:809
game_data * gamedata
Definition: resources.cpp:22
map_location loc_
const config * cfg_
Used to access our config (original or copy, as appropriate).
Definition: variable.hpp:209
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:911
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:44
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:37
const std::string & name() const
Definition: variable.hpp:234
int wml_y() const
Definition: location.hpp:158
all_children_iterator ordered_end() const
Definition: variable.cpp:482
bool memory_managed() const
Returns true if *this has made a copy of its config.
Definition: variable.hpp:203
game_board * gameboard
Definition: resources.cpp:20
bool operator==(const all_children_iterator &i) const
Definition: variable.cpp:472
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:207
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:210
config & store(const config &var_value=config())
Definition: variable.cpp:496
std::vector< scoped_wml_variable * > scoped_variables
Definition: game_data.hpp:33
std::size_t i
Definition: function.cpp:933
logger & err()
Definition: log.cpp:78
config::attribute_value expand(const std::string &) const
Definition: variable.cpp:349
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:82
static map_location::DIRECTION s
static tcache cache
Definition: minimap.cpp:134
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:102
CURSOR_TYPE get()
Definition: cursor.cpp:215
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:476
#define ERR_NG
Definition: variable.cpp:38
const_all_children_iterator ordered_begin() const
Definition: config.cpp:901
Information on a WML variable.
double t
Definition: astarsearch.cpp:64
const std::pair< std::string, vconfig > value_type
Definition: variable.hpp:145
const config & cfg_
Definition: variable.hpp:28
A variable-expanding proxy for the config class.
Definition: variable.hpp:44
const config & get_config() const
Definition: variable.hpp:82
Standard logging facilities (interface).
V::result_type apply_visitor(const V &visitor) const
Applies a visitor to the underlying variant.
std::string message
Definition: exceptions.hpp:31
static vconfig empty_vconfig()
Definition: variable.cpp:141
const variable_set * variables_
Definition: variable.hpp:210
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:96
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:211
std::vector< vconfig > child_list
Definition: variable.hpp:85
pointer operator->() const
Definition: variable.cpp:363
config_attribute_value attribute_value
Variant for storing WML attributes.
Definition: config.hpp:219
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:68
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:240
static map_location::DIRECTION n
virtual ~scoped_wml_variable()
Definition: variable.cpp:517
reference operator*() const
Definition: variable.cpp:432
const value_type reference
Definition: variable.hpp:151
all_children_iterator(const Itor &i, const variable_set &vars)
Definition: variable.cpp:371
all_children_iterator & operator++()
Definition: variable.cpp:381