The Battle for Wesnoth  1.19.0-dev
variable_info.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2005 - 2024
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 #include "variable_info.hpp"
19 
20 #include <utility>
21 
23 {
24 /**
25  * Helper function to apply the result of a specified visitor to a variable_info object.
26  *
27  * @tparam V Visitor type.
28  * @tparam T Visitor argument parameter pack.
29  *
30  * @param state Info state (the actual variable data).
31  * @param args Arguments to forward to visitor constructor.
32  *
33  * @returns Visitor output in its specified type.
34  * @throws std::range_error If @a state has an invalid type_ field.
35  */
36 template<typename V, typename... T>
37 typename V::result_t apply_visitor(typename V::param_t state, T&&... args)
38 {
39  static_assert(std::is_base_of<
41  typename V::result_t,
42  std::remove_reference_t<typename V::param_t>>,
43  V>::value, "Invalid visitor type.");
44 
45  // Create the visitor.
46  V visitor(std::forward<T>(args)...);
47 
48  switch(state.type_) {
49  case state_start:
50  return visitor.from_start(state);
51  case state_named:
52  return visitor.from_named(state);
53  case state_indexed:
54  return visitor.from_indexed(state);
55  case state_temporary:
56  return visitor.from_temporary(state);
57  }
58 
59  throw std::range_error("Failed to convert the TVisitor::param_t type");
60 }
61 } // end namespace variable_info_implementation
62 
63 using namespace variable_info_implementation;
64 
65 template<typename V>
66 variable_info<V>::variable_info(const std::string& varname, maybe_const_t<config, V>& vars) noexcept
67  : name_(varname)
68  , state_(vars)
69  , valid_(true)
70 {
71  try {
72  calculate_value();
73  } catch(const invalid_variablename_exception&) {
74  valid_ = false;
75  }
76 }
77 
78 template<typename V>
80 {
81  std::size_t previous_index = 0, name_size = name_.size();
82 
83  for(std::size_t loop_index = 0; loop_index < name_size; loop_index++) {
84  switch(name_[loop_index]) {
85  case '.':
86  case '[':
87  /* '.' and '[' mark the end of a string key.
88  * The result is obviously that '.' and '[' are
89  * treated equally so 'aaa.9].bbbb[zzz.uu.7]'
90  * is interpreted as 'aaa[9].bbbb.zzz.uu[7]'
91  * Use is_valid_variable function for stricter variable name checking.
92  */
93  apply_visitor<get_variable_key_visitor<V>>(
94  state_, name_.substr(previous_index, loop_index - previous_index));
95 
96  previous_index = loop_index + 1;
97  break;
98  case ']':
99  // ']' marks the end of an integer key.
100  apply_visitor<get_variable_index_visitor<V>>(state_, parse_index(&name_[previous_index]));
101 
102  // After ']' we always expect a '.' or the end of the string
103  // Ignore the next char which is a '.'
104  loop_index++;
105  if(loop_index < name_.length() && name_[loop_index] != '.') {
107  }
108 
109  previous_index = loop_index + 1;
110  break;
111  default:
112  break;
113  }
114  }
115 
116  if(previous_index != name_.length() + 1) {
117  // The string didn't end with ']'
118  // In this case we still didn't add the key behind the last '.'
119  apply_visitor<get_variable_key_visitor<V>>(state_, name_.substr(previous_index));
120  }
121 }
122 
123 template<typename V>
125 {
126  throw_on_invalid();
127  return state_.type_ == state_start || state_.type_ == state_indexed;
128 }
129 
130 template<typename V>
132 {
133  throw_on_invalid();
134  return apply_visitor<as_scalar_visitor<V>>(state_);
135 }
136 
137 template<typename V>
139 {
140  throw_on_invalid();
141  return apply_visitor<as_container_visitor<V>>(state_);
142 }
143 
144 template<typename V>
146 {
147  throw_on_invalid();
148  return apply_visitor<as_array_visitor<V>>(state_);
149 }
150 
151 template<typename V>
153 {
154  if(!valid_) {
156  }
157 }
158 
159 template<typename V>
161 {
162  return V::error_message(name_);
163 }
164 
165 template<typename V>
167 {
168  throw_on_invalid();
169  return (state_.type_ == state_temporary)
170  || ((state_.type_ == state_named) && state_.child_->has_attribute(state_.key_));
171 }
172 
173 template<typename V>
175 {
176  throw_on_invalid();
177  return apply_visitor<exists_as_container_visitor<V>>(state_);
178 }
179 
180 template<typename V>
181 void variable_info_mutable<V>::clear(bool only_tables) const
182 {
183  this->throw_on_invalid();
184  return apply_visitor<clear_value_visitor<V>>(this->state_, only_tables);
185 }
186 
187 /**
188  * In order to allow the appropriate 'children' argument to forward through to the appropriate
189  * ctor of type T in as_range_visitor_base via apply_visitor, we need to specify the argument
190  * type as an rvalue reference to lvalue reference in order to collapse to an lvalue reference.
191  *
192  * Using a convenient template alias for convenience.
193  */
194 template<typename V, typename T>
196 
197 template<typename V>
199 {
200  this->throw_on_invalid();
201  return apply_visitor<range_visitor_wrapper<V, append_range_h>>(this->state_, children);
202 }
203 
204 template<typename V>
206 {
207  this->throw_on_invalid();
208  return apply_visitor<range_visitor_wrapper<V, insert_range_h>>(this->state_, children);
209 }
210 
211 template<typename V>
213 {
214  this->throw_on_invalid();
215  return apply_visitor<range_visitor_wrapper<V, replace_range_h>>(this->state_, children);
216 }
217 
218 template<typename V>
219 void variable_info_mutable<V>::merge_array(std::vector<config> children) const
220 {
221  this->throw_on_invalid();
222  apply_visitor<range_visitor_wrapper<V, merge_range_h>>(this->state_, children);
223 }
224 
225 // Force compilation of the following template instantiations
227 template class variable_info<vi_policy_create>;
228 template class variable_info<vi_policy_throw>;
229 
boost::iterator_range< child_iterator > child_itors
Definition: config.hpp:282
Additional functionality for a non-const variable_info.
config::child_itors insert_array(std::vector< config > children) const
void merge_array(std::vector< config > children) const
config::child_itors replace_array(std::vector< config > children) const
void clear(bool only_tables=false) const
Clears the value this object points to.
config::child_itors append_array(std::vector< config > children) const
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...
std::string get_error_message() const
void throw_on_invalid() const
bool exists_as_container() const
variable_info(const std::string &varname, maybe_const_t< config, V > &vars) noexcept
void calculate_value()
maybe_const_t< config, V > & as_container() const
If instantiated with vi_policy_const, the lifetime of the returned const attribute_value reference mi...
bool explicit_index() const
bool exists_as_attribute() const
int parse_index(const char *index_str)
Parses a ']' terminated string.
V::result_t apply_visitor(typename V::param_t state, T &&... args)
Helper function to apply the result of a specified visitor to a variable_info object.
@ state_named
The result of .someval.
@ state_start
Represents the initial variable state before processing.
@ state_indexed
The result of .someval[index].
@ state_temporary
The result of .length.
typename variable_info_implementation::maybe_const< T, V >::type maybe_const_t
Helper template alias for maybe_const, defined at global scope for convenience.