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