The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
variable_info.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#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 */
36template<typename V, typename... T>
37typename 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
63using namespace variable_info_implementation;
64
65template<typename V>
66variable_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
78template<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
123template<typename V>
125{
126 throw_on_invalid();
127 return state_.type_ == state_start || state_.type_ == state_indexed;
128}
129
130template<typename V>
132{
133 throw_on_invalid();
134 return apply_visitor<as_scalar_visitor<V>>(state_);
135}
136
137template<typename V>
139{
140 throw_on_invalid();
141 return apply_visitor<as_container_visitor<V>>(state_);
142}
143
144template<typename V>
146{
147 throw_on_invalid();
148 return apply_visitor<as_array_visitor<V>>(state_);
149}
150
151template<typename V>
153{
154 if(!valid_) {
156 }
157}
158
159template<typename V>
161{
162 return V::error_message(name_);
163}
164
165template<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
173template<typename V>
175{
176 throw_on_invalid();
177 return apply_visitor<exists_as_container_visitor<V>>(state_);
178}
179
180template<typename V>
181void 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 */
194template<typename V, typename T>
196
197template<typename V>
199{
200 this->throw_on_invalid();
201 return apply_visitor<range_visitor_wrapper<V, append_range_h>>(this->state_, children);
202}
203
204template<typename V>
206{
207 this->throw_on_invalid();
208 return apply_visitor<range_visitor_wrapper<V, insert_range_h>>(this->state_, children);
209}
210
211template<typename V>
213{
214 this->throw_on_invalid();
215 return apply_visitor<range_visitor_wrapper<V, replace_range_h>>(this->state_, children);
216}
217
218template<typename V>
219void 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
228template class variable_info<vi_policy_throw>;
229
boost::iterator_range< child_iterator > child_itors
Definition config.hpp:280
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
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.
std::unique_ptr< MIX_Audio, decltype(&MIX_DestroyAudio)> value
Definition sound.cpp:139
typename variable_info_implementation::maybe_const< T, V >::type maybe_const_t
Helper template alias for maybe_const, defined at global scope for convenience.