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