The Battle for Wesnoth  1.19.0-dev
config_attribute_value.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2024
3  by Guillaume Melquiond <guillaume.melquiond@gmail.com>
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  * Routines related to configuration-files / WML.
20  */
21 
23 
24 #include "lexical_cast.hpp"
25 #include "log.hpp"
27 
28 #include <cstdlib>
29 
30 static lg::log_domain log_config("config");
31 #define ERR_CF LOG_STREAM(err, log_config)
32 #define DBG_CF LOG_STREAM(debug, log_config)
33 
34 // Special string values.
35 const std::string config_attribute_value::s_yes("yes");
36 const std::string config_attribute_value::s_no("no");
37 const std::string config_attribute_value::s_true("true");
38 const std::string config_attribute_value::s_false("false");
39 
40 /** Default implementation, but defined out-of-line for efficiency reasons. */
42  : value_()
43 {
44 }
45 
46 /** Default implementation, but defined out-of-line for efficiency reasons. */
48 {
49 }
50 
51 /** Default implementation, but defined out-of-line for efficiency reasons. */
53  : value_(that.value_)
54 {
55 }
56 
57 /** Default implementation, but defined out-of-line for efficiency reasons. */
59 {
60  value_ = that.value_;
61  return *this;
62 }
63 
65 {
66  value_ = yes_no(v);
67  return *this;
68 }
69 
71 {
72  value_ = v;
73  return *this;
74 }
75 
77 {
78  if(v > 0) {
79  // We can store this unsigned.
80  return *this = static_cast<unsigned long long>(v);
81  }
82 
83  if(v >= INT_MIN) {
84  // We can store this as an int.
85  return *this = static_cast<int>(v);
86  }
87 
88  // Getting to this point should be rare. (Currently, getting here means
89  // something like there was so much draining in a campaign that the
90  // total damage taken is not only negative, but so negative that an
91  // int cannot hold the value.) So rare that it is not worth precise
92  // treatment; just use a double.
93  value_ = static_cast<double>(v);
94  return *this;
95 }
96 
98 {
99  // Use int for smaller numbers.
100  if(v <= INT_MAX) {
101  return *this = static_cast<int>(v);
102  }
103 
104  value_ = v;
105  return *this;
106 }
107 
109 {
110  // Try to store integers in other types.
111  if(v > 0.0) {
112  // Convert to unsigned and pass this off to that assignment operator.
113  unsigned long long ull = static_cast<unsigned long long>(v);
114  if(static_cast<double>(ull) == v) {
115  return *this = ull;
116  }
117  } else {
118  // Convert to integer and pass this off to that assignment operator.
119  int i = static_cast<int>(v);
120  if(static_cast<double>(i) == v) {
121  return *this = i;
122  }
123  }
124 
125  // If we get here, this does in fact get stored as a double.
126  value_ = v;
127  return *this;
128 }
129 
130 namespace
131 {
132 /**
133  * Attempts to convert @a source to the template type.
134  * This is to avoid "overzealous reinterpretations of certain WML strings as numeric types".
135  * For example: the version "2.1" and "2.10" are not the same.
136  * Another example: the string "0001" given to [message] should not be displayed to the player as just "1".
137  * @returns true if the conversion was successful and the source string
138  * can be reobtained by streaming the result.
139  */
140 template<typename To>
141 bool from_string_verify(const std::string& source, To& res)
142 {
143  // Check 1: convertible to the target type.
144  std::istringstream in_str(source);
145  if(!(in_str >> res)) {
146  return false;
147  }
148 
149  // Check 2: convertible back to the same string.
150  std::ostringstream out_str;
151  out_str << res;
152  return out_str.str() == source;
153 }
154 } // end anon namespace
155 
157 {
158  // Handle some special strings.
159  if(v.empty()) {
160  value_ = v;
161  return *this;
162  }
163 
164  if(v == s_yes) {
165  value_ = yes_no(true);
166  return *this;
167  }
168 
169  if(v == s_no) {
170  value_ = yes_no(false);
171  return *this;
172  }
173 
174  if(v == s_true) {
175  value_ = true_false(true);
176  return *this;
177  }
178 
179  if(v == s_false) {
180  value_ = true_false(false);
181  return *this;
182  }
183 
184  // Attempt to convert to a number.
185  char* eptr;
186  double d = strtod(v.c_str(), &eptr);
187  if(*eptr == '\0') {
188  // Possibly a number. See what type it should be stored in.
189  // (All conversions will be from the string since the largest integer
190  // type could have more precision than a double.)
191  if(d > 0.0) {
192  // The largest type for positive integers is unsigned long long.
193  unsigned long long ull = 0;
194  if(from_string_verify<unsigned long long>(v, ull)) {
195  return *this = ull;
196  }
197  } else {
198  // The largest (variant) type for negative integers is int.
199  int i = 0;
200  if(from_string_verify<int>(v, i)) {
201  return *this = i;
202  }
203  }
204 
205  // This does not look like an integer, so it should be a double.
206  // However, make sure it can convert back to the same string (in
207  // case this is a string that just looks like a numeric value).
208  std::ostringstream tester;
209  tester << d;
210  if(tester.str() == v) {
211  value_ = d;
212  return *this;
213  }
214  }
215 
216  // No conversion possible. Store the string.
217  value_ = v;
218  return *this;
219 }
220 
222 {
223  // TODO: Currently this acts just like std::string assignment.
224  // Perhaps the underlying variant should take a string_view directly?
225  return operator=(std::string(v));
226 
227 }
229 {
230  if(!v.translatable()) {
231  return *this = v.str();
232  }
233 
234  value_ = v;
235  return *this;
236 }
237 
239 {
240  if(!v.empty()) {
241  *this = v;
242  }
243 }
244 
246 {
247  if(!v.empty()) {
248  *this = v;
249  }
250 }
251 
253 {
254  if(const yes_no* p = utils::get_if<yes_no>(&value_))
255  return *p;
256  if(const true_false* p = utils::get_if<true_false>(&value_))
257  return *p;
258 
259  // No other types are ever recognized as boolean.
260  return def;
261 }
262 
263 namespace
264 {
265 /** Visitor for converting a variant to a numeric type (T). */
266 template<typename T>
267 class attribute_numeric_visitor
268 #ifdef USING_BOOST_VARIANT
269  : public boost::static_visitor<T>
270 #endif
271 {
272 public:
273  // Constructor stores the default value.
274  attribute_numeric_visitor(T def) : def_(def) {}
275 
276  T operator()(const utils::monostate&) const { return def_; }
277  T operator()(bool) const { return def_; }
278  T operator()(int i) const { return static_cast<T>(i); }
279  T operator()(unsigned long long u) const { return static_cast<T>(u); }
280  T operator()(double d) const { return static_cast<T>(d); }
281  T operator()(const std::string& s) const { return lexical_cast_default<T>(s, def_); }
282  T operator()(const t_string&) const { return def_; }
283 
284 private:
285  const T def_;
286 };
287 } // end anon namespace
288 
290 {
291  return apply_visitor(attribute_numeric_visitor<int>(def));
292 }
293 
294 long long config_attribute_value::to_long_long(long long def) const
295 {
296  return apply_visitor(attribute_numeric_visitor<long long>(def));
297 }
298 
299 unsigned config_attribute_value::to_unsigned(unsigned def) const
300 {
301  return apply_visitor(attribute_numeric_visitor<unsigned>(def));
302 }
303 
304 std::size_t config_attribute_value::to_size_t(std::size_t def) const
305 {
306  return apply_visitor(attribute_numeric_visitor<std::size_t>(def));
307 }
308 
309 std::time_t config_attribute_value::to_time_t(std::time_t def) const
310 {
311  return apply_visitor(attribute_numeric_visitor<std::time_t>(def));
312 }
313 
314 double config_attribute_value::to_double(double def) const
315 {
316  return apply_visitor(attribute_numeric_visitor<double>(def));
317 }
318 
319 /** Visitor for converting a variant to a string. */
321 #ifdef USING_BOOST_VARIANT
322  : public boost::static_visitor<std::string>
323 #endif
324 {
325  const std::string default_;
326 
327 public:
328  string_visitor(const std::string& fallback) : default_(fallback) {}
329 
330  std::string operator()(const utils::monostate &) const { return default_; }
331  std::string operator()(const yes_no & b) const { return b.str(); }
332  std::string operator()(const true_false & b) const { return b.str(); }
333  std::string operator()(int i) const { return std::to_string(i); }
334  std::string operator()(unsigned long long u) const { return std::to_string(u); }
335  std::string operator()(double d) const { return lexical_cast<std::string>(d); }
336  std::string operator()(const std::string& s) const { return s; }
337  std::string operator()(const t_string& s) const { return s.str(); }
338 };
339 
340 std::string config_attribute_value::str(const std::string& fallback) const
341 {
342  return apply_visitor(string_visitor(fallback));
343 }
344 
346 {
347  if(const t_string* p = utils::get_if<t_string>(&value_)) {
348  return *p;
349  }
350 
351  return str();
352 }
353 
354 /**
355  * Tests for an attribute that was never set.
356  */
358 {
359  return utils::holds_alternative<utils::monostate>(value_);
360 }
361 
362 /**
363  * Tests for an attribute that either was never set or was set to "".
364  */
366 {
367  if(blank()) {
368  return true;
369  }
370 
371  if(const std::string* p = utils::get_if<std::string>(&value_)) {
372  return p->empty();
373  }
374 
375  return false;
376 }
377 
378 /** Visitor handling equality checks. */
380 #ifdef USING_BOOST_VARIANT
381  : public boost::static_visitor<bool>
382 #endif
383 {
384 public:
385  // Most generic: not equal.
386  template<typename T, typename U>
387  bool operator()(const T&, const U&) const
388  {
389  return false;
390  }
391 
392  // Same types are comparable and might be equal.
393  template<typename T>
394  bool operator()(const T& lhs, const T& rhs) const
395  {
396  return lhs == rhs;
397  }
398 
399  // Boolean values can be compared.
400  bool operator()(const true_false& lhs, const yes_no& rhs) const
401  {
402  return bool(lhs) == bool(rhs);
403  }
404 
405  bool operator()(const yes_no& lhs, const true_false& rhs) const
406  {
407  return bool(lhs) == bool(rhs);
408  }
409 };
410 
411 /**
412  * Checks for equality of the attribute values when viewed as strings.
413  * Exception: Boolean synonyms can be equal ("yes" == "true").
414  * Note: Blanks have no string representation, so do not equal "" (an empty string).
415  */
417 {
418  return utils::visit(equality_visitor(), value_, other.value_);
419 }
420 
421 /**
422  * Checks for equality of the attribute values when viewed as strings.
423  * Exception: Boolean synonyms can be equal ("yes" == "true").
424  * Note: Blanks have no string representation, so do not equal "" (an empty string).
425  * Also note that translatable string are never equal to non translatable strings.
426  */
427 bool config_attribute_value::equals(const std::string& str) const
428 {
430  v = str;
431  return *this == v;
432  // if c["a"] = "1" then this solution would have resulted in c["a"] == "1" being false
433  // because a["a"] is '1' and not '"1"'.
434  // return boost::apply_visitor(std::bind( equality_visitor(), std::placeholders::_1, std::cref(str) ), value_);
435  // that's why we don't use it.
436 }
437 
438 std::ostream& operator<<(std::ostream& os, const config_attribute_value& v)
439 {
440  // Simple implementation, but defined out-of-line because of the templating
441  // involved.
442  v.apply_visitor([&os](const auto& val) { os << val; });
443  return os;
444 }
445 
446 namespace utils
447 {
448  std::vector<std::string> split(const config_attribute_value& val) {
449  return utils::split(val.str());
450  }
451 }
bool operator()(const yes_no &lhs, const true_false &rhs) const
bool operator()(const T &, const U &) const
bool operator()(const true_false &lhs, const yes_no &rhs) const
bool operator()(const T &lhs, const T &rhs) const
Visitor for converting a variant to a string.
std::string operator()(unsigned long long u) const
std::string operator()(const utils::monostate &) const
std::string operator()(const t_string &s) const
std::string operator()(const true_false &b) const
std::string operator()(const yes_no &b) const
std::string operator()(const std::string &s) const
A wrapper for bool to get the correct streaming ("true"/"false").
A wrapper for bool to get the correct streaming ("yes"/"no").
Variant for storing WML attributes.
config_attribute_value & operator=(const config_attribute_value &)
Default implementation, but defined out-of-line for efficiency reasons.
bool equals(const std::string &str) const
Checks for equality of the attribute values when viewed as strings.
static const std::string s_false
value_type value_
The stored value will always use the first type from the variant definition that can represent it and...
std::string str(const std::string &fallback="") const
std::time_t to_time_t(std::time_t def=0) const
static const std::string s_true
auto apply_visitor(const V &visitor) const
Visitor support: Applies a visitor to the underlying variant.
unsigned to_unsigned(unsigned def=0) const
static const std::string s_yes
bool blank() const
Tests for an attribute that was never set.
static const std::string s_no
config_attribute_value()
Default implementation, but defined out-of-line for efficiency reasons.
bool to_bool(bool def=false) const
~config_attribute_value()
Default implementation, but defined out-of-line for efficiency reasons.
bool operator==(const config_attribute_value &other) const
Checks for equality of the attribute values when viewed as strings.
void write_if_not_empty(const std::string &v)
Calls operator=(const std::string&) if v is not empty.
bool empty() const
Tests for an attribute that either was never set or was set to "".
long long to_long_long(long long def=0) const
std::size_t to_size_t(std::size_t def=0) const
double to_double(double def=0.) const
bool translatable() const
Definition: tstring.hpp:192
bool empty() const
Definition: tstring.hpp:186
const std::string & str() const
Definition: tstring.hpp:190
std::ostream & operator<<(std::ostream &os, const config_attribute_value &v)
static lg::log_domain log_config("config")
Definitions for the interface to Wesnoth Markup Language (WML).
std::size_t i
Definition: function.cpp:968
New lexcical_cast header.
Standard logging facilities (interface).
std::vector< std::string > split(const config_attribute_value &val)
mock_party p
static map_location::DIRECTION s
#define d
#define b