The Battle for Wesnoth  1.15.0-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"
25 #include "utils/const_clone.hpp"
26 #include "utils/functional.hpp"
27 
28 #include <cstdlib>
29 #include <cstring>
30 #include <deque>
31 #include <istream>
32 
33 #include <boost/variant/apply_visitor.hpp>
34 #include <boost/variant/get.hpp>
35 #include <boost/variant/static_visitor.hpp>
36 #include <boost/variant/variant.hpp>
37 
38 static lg::log_domain log_config("config");
39 #define ERR_CF LOG_STREAM(err, log_config)
40 #define DBG_CF LOG_STREAM(debug, log_config)
41 
42 // Special string values.
43 const std::string config_attribute_value::s_yes("yes");
44 const std::string config_attribute_value::s_no("no");
45 const std::string config_attribute_value::s_true("true");
46 const std::string config_attribute_value::s_false("false");
47 
48 /** Default implementation, but defined out-of-line for efficiency reasons. */
50  : value_()
51 {
52 }
53 
54 /** Default implementation, but defined out-of-line for efficiency reasons. */
56 {
57 }
58 
59 /** Default implementation, but defined out-of-line for efficiency reasons. */
61  : value_(that.value_)
62 {
63 }
64 
65 /** Default implementation, but defined out-of-line for efficiency reasons. */
67 {
68  value_ = that.value_;
69  return *this;
70 }
71 
73 {
74  value_ = yes_no(v);
75  return *this;
76 }
77 
79 {
80  value_ = v;
81  return *this;
82 }
83 
85 {
86  if(v > 0) {
87  // We can store this unsigned.
88  return *this = static_cast<unsigned long long>(v);
89  }
90 
91  if(v >= INT_MIN) {
92  // We can store this as an int.
93  return *this = static_cast<int>(v);
94  }
95 
96  // Getting to this point should be rare. (Currently, getting here means
97  // something like there was so much draining in a campaign that the
98  // total damage taken is not only negative, but so negative that an
99  // int cannot hold the value.) So rare that it is not worth precise
100  // treatment; just use a double.
101  value_ = static_cast<double>(v);
102  return *this;
103 }
104 
106 {
107  // Use int for smaller numbers.
108  if(v <= INT_MAX) {
109  return *this = static_cast<int>(v);
110  }
111 
112  value_ = v;
113  return *this;
114 }
115 
117 {
118  // Try to store integers in other types.
119  if(v > 0.0) {
120  // Convert to unsigned and pass this off to that assignment operator.
121  unsigned long long ull = static_cast<unsigned long long>(v);
122  if(static_cast<double>(ull) == v) {
123  return *this = ull;
124  }
125  } else {
126  // Convert to integer and pass this off to that assignment operator.
127  int i = static_cast<int>(v);
128  if(static_cast<double>(i) == v) {
129  return *this = i;
130  }
131  }
132 
133  // If we get here, this does in fact get stored as a double.
134  value_ = v;
135  return *this;
136 }
137 
138 namespace
139 {
140 /**
141  * Attempts to convert @a source to the template type.
142  * This is to avoid "overzealous reinterpretations of certain WML strings as
143  * numeric types" (c.f. bug #19201).
144  * @returns true if the conversion was successful and the source string
145  * can be reobtained by streaming the result.
146  */
147 template<typename To>
148 bool from_string_verify(const std::string& source, To& res)
149 {
150  // Check 1: convertible to the target type.
151  std::istringstream in_str(source);
152  if(!(in_str >> res)) {
153  return false;
154  }
155 
156  // Check 2: convertible back to the same string.
157  std::ostringstream out_str;
158  out_str << res;
159  return out_str.str() == source;
160 }
161 } // end anon namespace
162 
164 {
165  // Handle some special strings.
166  if(v.empty()) {
167  value_ = v;
168  return *this;
169  }
170 
171  if(v == s_yes) {
172  value_ = yes_no(true);
173  return *this;
174  }
175 
176  if(v == s_no) {
177  value_ = yes_no(false);
178  return *this;
179  }
180 
181  if(v == s_true) {
182  value_ = true_false(true);
183  return *this;
184  }
185 
186  if(v == s_false) {
187  value_ = true_false(false);
188  return *this;
189  }
190 
191  // Attempt to convert to a number.
192  char* eptr;
193  double d = strtod(v.c_str(), &eptr);
194  if(*eptr == '\0') {
195  // Possibly a number. See what type it should be stored in.
196  // (All conversions will be from the string since the largest integer
197  // type could have more precision than a double.)
198  if(d > 0.0) {
199  // The largest type for positive integers is unsigned long long.
200  unsigned long long ull = 0;
201  if(from_string_verify<unsigned long long>(v, ull)) {
202  return *this = ull;
203  }
204  } else {
205  // The largest (variant) type for negative integers is int.
206  int i = 0;
207  if(from_string_verify<int>(v, i)) {
208  return *this = i;
209  }
210  }
211 
212  // This does not look like an integer, so it should be a double.
213  // However, make sure it can convert back to the same string (in
214  // case this is a string that just looks like a numeric value).
215  std::ostringstream tester;
216  tester << d;
217  if(tester.str() == v) {
218  value_ = d;
219  return *this;
220  }
221  }
222 
223  // No conversion possible. Store the string.
224  value_ = v;
225  return *this;
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(const yes_no* p = boost::get<const yes_no>(&value_))
248  return *p;
249  if(const true_false* p = boost::get<const true_false>(&value_))
250  return *p;
251 
252  // No other types are ever recognized as boolean.
253  return def;
254 }
255 
256 namespace
257 {
258 /// Visitor for converting a variant to a numeric type (T).
259 template<typename T>
260 class attribute_numeric_visitor : public boost::static_visitor<T>
261 {
262 public:
263  // Constructor stores the default value.
264  attribute_numeric_visitor(T def) : def_(def) {}
265 
266  T operator()(const boost::blank&) const { return def_; }
267  T operator()(bool) const { return def_; }
268  T operator()(int i) const { return static_cast<T>(i); }
269  T operator()(unsigned long long u) const { return static_cast<T>(u); }
270  T operator()(double d) const { return static_cast<T>(d); }
271  T operator()(const std::string& s) const { return lexical_cast_default<T>(s, def_); }
272  T operator()(const t_string&) const { return def_; }
273 
274 private:
275  const T def_;
276 };
277 } // end anon namespace
278 
280 {
281  return apply_visitor(attribute_numeric_visitor<int>(def));
282 }
283 
284 long long config_attribute_value::to_long_long(long long def) const
285 {
286  return apply_visitor(attribute_numeric_visitor<long long>(def));
287 }
288 
289 unsigned config_attribute_value::to_unsigned(unsigned def) const
290 {
291  return apply_visitor(attribute_numeric_visitor<unsigned>(def));
292 }
293 
294 std::size_t config_attribute_value::to_size_t(std::size_t def) const
295 {
296  return apply_visitor(attribute_numeric_visitor<std::size_t>(def));
297 }
298 
299 std::time_t config_attribute_value::to_time_t(std::time_t def) const
300 {
301  return apply_visitor(attribute_numeric_visitor<std::time_t>(def));
302 }
303 
304 double config_attribute_value::to_double(double def) const
305 {
306  return apply_visitor(attribute_numeric_visitor<double>(def));
307 }
308 
309 /// Visitor for converting a variant to a string.
310 class config_attribute_value::string_visitor : public boost::static_visitor<std::string>
311 {
312  const std::string default_;
313 
314 public:
315  string_visitor(const std::string& fallback) : default_(fallback) {}
316 
317  std::string operator()(const boost::blank &) const { return default_; }
318  std::string operator()(const yes_no & b) const { return b.str(); }
319  std::string operator()(const true_false & b) const { return b.str(); }
320  std::string operator()(int i) const { return lexical_cast<std::string>(i); }
321  std::string operator()(unsigned long long u) const { return lexical_cast<std::string>(u); }
322  std::string operator()(double d) const { return lexical_cast<std::string>(d); }
323  std::string operator()(const std::string& s) const { return s; }
324  std::string operator()(const t_string& s) const { return s.str(); }
325 };
326 
327 std::string config_attribute_value::str(const std::string& fallback) const
328 {
329  return apply_visitor(string_visitor(fallback));
330 }
331 
333 {
334  if(const t_string* p = boost::get<const t_string>(&value_)) {
335  return *p;
336  }
337 
338  return str();
339 }
340 
341 /**
342  * Tests for an attribute that was never set.
343  */
345 {
346  return boost::get<const boost::blank>(&value_) != nullptr;
347 }
348 
349 /**
350  * Tests for an attribute that either was never set or was set to "".
351  */
353 {
354  if(boost::get<const boost::blank>(&value_)) {
355  return true;
356  }
357 
358  if(const std::string* p = boost::get<const std::string>(&value_)) {
359  return p->empty();
360  }
361 
362  return false;
363 }
364 
365 /// Visitor handling equality checks.
366 class config_attribute_value::equality_visitor : public boost::static_visitor<bool>
367 {
368 public:
369  // Most generic: not equal.
370  template<typename T, typename U>
371  bool operator()(const T&, const U&) const
372  {
373  return false;
374  }
375 
376  // Same types are comparable and might be equal.
377  template<typename T>
378  bool operator()(const T& lhs, const T& rhs) const
379  {
380  return lhs == rhs;
381  }
382 
383  // Boolean values can be compared.
384  bool operator()(const true_false& lhs, const yes_no& rhs) const
385  {
386  return bool(lhs) == bool(rhs);
387  }
388 
389  bool operator()(const yes_no& lhs, const true_false& rhs) const
390  {
391  return bool(lhs) == bool(rhs);
392  }
393 };
394 
395 /**
396  * Checks for equality of the attribute values when viewed as strings.
397  * Exception: Boolean synonyms can be equal ("yes" == "true").
398  * Note: Blanks have no string representation, so do not equal "" (an empty string).
399  */
401 {
403 }
404 
405 /**
406  * Checks for equality of the attribute values when viewed as strings.
407  * Exception: Boolean synonyms can be equal ("yes" == "true").
408  * Note: Blanks have no string representation, so do not equal "" (an empty string).
409  * Also note that translatable string are never equal to non translatable strings.
410  */
411 bool config_attribute_value::equals(const std::string& str) const
412 {
414  v = str;
415  return *this == v;
416  // if c["a"] = "1" then this solution would have resulted in c["a"] == "1" being false
417  // because a["a"] is '1' and not '"1"'.
418  // return boost::apply_visitor(std::bind( equality_visitor(), _1, std::cref(str) ), value_);
419  // that's why we don't use it.
420 }
421 
422 std::ostream& operator<<(std::ostream& os, const config_attribute_value& v)
423 {
424  // Simple implementation, but defined out-of-line because of the templating
425  // involved.
426  return os << v.value_;
427 }
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...
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