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