The Battle for Wesnoth  1.15.2+dev
config_attribute_value.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2018 by David White <dave@whitevine.net>
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 /**
16  * @file
17  * Definitions for the interface to Wesnoth Markup Language (WML).
18  *
19  * This module defines the interface to Wesnoth Markup Language (WML). WML is
20  * a simple hierarchical text-based file format. The format is defined in
21  * Wiki, under BuildingScenariosWML
22  *
23  * All configuration files are stored in this format, and data is sent across
24  * the network in this format. It is thus used extensively throughout the
25  * game.
26  */
27 
28 #pragma once
29 
30 #include "global.hpp"
31 
32 #include <climits>
33 #include <ctime>
34 #include <iosfwd>
35 #include <iterator>
36 #include <map>
37 #include <string>
38 #include <utility>
39 #include <vector>
40 #include <type_traits>
41 #include <memory>
42 
43 #include <boost/exception/exception.hpp>
44 #include <boost/variant/apply_visitor.hpp>
45 #include <boost/variant/variant.hpp>
46 #include <boost/range/iterator_range.hpp>
47 
48 #include "tstring.hpp"
49 
50 class enum_tag;
51 
52 /**
53  * Variant for storing WML attributes.
54  * The most efficient type is used when assigning a value. For instance,
55  * strings "yes", "no", "true", "false" will be detected and stored as boolean.
56  * @note The blank variant is only used when querying missing attributes.
57  * It is not stored in config objects.
58  */
60 {
61  /// A wrapper for bool to get the correct streaming ("true"/"false").
62  /// Most visitors can simply treat this as bool.
63 public:
64  class true_false
65  {
66  bool value_;
67  public:
68  explicit true_false(bool value = false) : value_(value) {}
69  operator bool() const { return value_; }
70 
71  const std::string & str() const
72  {
74  }
75  };
76  friend std::ostream& operator<<(std::ostream &os, const true_false &v) { return os << v.str(); }
77 
78  /// A wrapper for bool to get the correct streaming ("yes"/"no").
79  /// Most visitors can simply treat this as bool.
80  class yes_no
81  {
82  bool value_;
83  public:
84  explicit yes_no(bool value = false) : value_(value) {}
85  operator bool() const { return value_; }
86 
87  const std::string & str() const
88  {
90  }
91  };
92  friend std::ostream& operator<<(std::ostream &os, const yes_no &v) { return os << v.str(); }
93 private:
94  /// Visitor for checking equality.
95  class equality_visitor;
96  /// Visitor for converting a variant to a string.
98 
99  // Data will be stored in a variant, allowing for the possibility of
100  // boolean, numeric, and translatable data in addition to basic string
101  // data. For most purposes, int is the preferred type for numeric data
102  // as it is fast (often natural word size). While it is desirable to
103  // use few types (to keep the overhead low), we do have use cases for
104  // fractions (double) and huge numbers (up to the larger of LLONG_MAX
105  // and SIZE_MAX).
106  typedef boost::variant<boost::blank,
108  int, unsigned long long, double,
109  std::string, t_string
110  > value_type;
111  /// The stored value will always use the first type from the variant
112  /// definition that can represent it and that can be streamed to the
113  /// correct string representation (if applicable).
114  /// This is enforced upon assignment.
115  value_type value_;
116 
117 public:
118  /// Default implementation, but defined out-of-line for efficiency reasons.
120  /// Default implementation, but defined out-of-line for efficiency reasons.
122  /// Default implementation, but defined out-of-line for efficiency reasons.
124  /// Default implementation, but defined out-of-line for efficiency reasons.
126 
127  // Numeric assignments:
130  config_attribute_value& operator=(long v) { return operator=(static_cast<long long>(v)); }
131  config_attribute_value& operator=(long long v);
132  config_attribute_value& operator=(unsigned v) { return operator=(static_cast<unsigned long long>(v)); }
133  config_attribute_value& operator=(unsigned long v) { return operator=(static_cast<unsigned long long>(v)); }
134  config_attribute_value& operator=(unsigned long long v);
136 
137  // String assignments:
138  config_attribute_value& operator=(const char *v) { return operator=(std::string(v)); }
139  config_attribute_value& operator=(const std::string &v);
141  template<typename T>
142  std::enable_if_t<std::is_base_of<enum_tag, T>::value, config_attribute_value&> operator=(const T &v)
143  {
144  return operator=(T::enum_to_string(v));
145  }
146 
147  /** Calls @ref operator=(const std::string&) if @a v is not empty. */
148  void write_if_not_empty(const std::string& v);
149 
150  // Extracting as a specific type:
151  bool to_bool(bool def = false) const;
152  int to_int(int def = 0) const;
153  long long to_long_long(long long def = 0) const;
154  unsigned to_unsigned(unsigned def = 0) const;
155  std::size_t to_size_t(std::size_t def = 0) const;
156  std::time_t to_time_t(std::time_t def = 0) const;
157  double to_double(double def = 0.) const;
158  std::string str(const std::string& fallback = "") const;
159  t_string t_str() const;
160  /**
161  @tparam T a type created with MAKE_ENUM macro
162  NOTE: since T::VALUE constants is not of type T but of the underlying enum type you must specify the template parameter explicitly
163  TODO: Fix this in c++11 using constexpr types.
164  */
165  template<typename T>
166  std::enable_if_t<std::is_base_of<enum_tag, T>::value, T> to_enum(const T &v) const
167  {
168  return T::string_to_enum(this->str(), v);
169  }
170 
171  // Implicit conversions:
172  operator int() const { return to_int(); }
173  operator std::string() const { return str(); }
174  operator t_string() const { return t_str(); }
175 
176  /// Tests for an attribute that was never set.
177  bool blank() const;
178  /// Tests for an attribute that either was never set or was set to "".
179  bool empty() const;
180 
181 
182  // Comparisons:
183  bool operator==(const config_attribute_value &other) const;
184  bool operator!=(const config_attribute_value &other) const
185  {
186  return !operator==(other);
187  }
188 
189  bool equals(const std::string& str) const;
190  // These function prevent t_string creation in case of c["a"] == "b" comparisons.
191  // The templates are needed to prevent using these function in case of c["a"] == 0 comparisons.
192  template<typename T>
193  std::enable_if_t<std::is_same<const std::string, std::add_const_t<T>>::value, bool>
194  friend operator==(const config_attribute_value &val, const T &str)
195  {
196  return val.equals(str);
197  }
198 
199  template<typename T>
200  std::enable_if_t<std::is_same<const char*, T>::value, bool>
201  friend operator==(const config_attribute_value& val, T str)
202  {
203  return val.equals(std::string(str));
204  }
205 
206  template<typename T>
207  bool friend operator==(const T& str, const config_attribute_value& val)
208  {
209  return val == str;
210  }
211 
212  template<typename T>
213  bool friend operator!=(const config_attribute_value& val, const T& str)
214  {
215  return !(val == str);
216  }
217 
218  template<typename T>
219  bool friend operator!=(const T &str, const config_attribute_value& val)
220  {
221  return !(val == str);
222  }
223 
224  // Streaming:
225  friend std::ostream& operator<<(std::ostream& os, const config_attribute_value& v);
226 
227  // Visitor support:
228  /// Applies a visitor to the underlying variant.
229  /// (See the documentation for Boost.Variant.)
230  template <typename V>
231  typename V::result_type apply_visitor(const V & visitor) const
232  {
233  return boost::apply_visitor(visitor, value_);
234  }
235 
236 private:
237  // Special strings.
238  static const std::string s_yes, s_no;
239  static const std::string s_true, s_false;
240 };
bool empty() const
Tests for an attribute that either was never set or was set to "".
bool friend operator!=(const T &str, const config_attribute_value &val)
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
config_attribute_value & operator=(unsigned v)
config_attribute_value & operator=(const config_attribute_value &)
Default implementation, but defined out-of-line for efficiency reasons.
Variant for storing WML attributes.
friend std::ostream & operator<<(std::ostream &os, const true_false &v)
const std::string & str() const
boost::variant< boost::blank, true_false, yes_no, int, unsigned long long, double, std::string, t_string > value_type
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 friend operator!=(const config_attribute_value &val, const T &str)
std::enable_if_t< std::is_same< const char *, T >::value, bool > friend operator==(const config_attribute_value &val, T str)
std::enable_if_t< std::is_base_of< enum_tag, T >::value, config_attribute_value & > operator=(const T &v)
std::enable_if_t< std::is_base_of< enum_tag, T >::value, T > to_enum(const T &v) const
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
config_attribute_value & operator=(unsigned long v)
void write_if_not_empty(const std::string &v)
Calls operator=(const std::string&) if v is not empty.
bool friend operator==(const T &str, const config_attribute_value &val)
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
config_attribute_value & operator=(long v)
V::result_type apply_visitor(const V &visitor) const
Applies a visitor to the underlying variant.
static const std::string s_true
config_attribute_value & operator=(const char *v)
bool operator==(const config_attribute_value &other) const
Checks for equality of the attribute values when viewed as strings.
std::enable_if_t< std::is_same< const std::string, std::add_const_t< T > >::value, bool > friend operator==(const config_attribute_value &val, const T &str)
bool operator!=(const config_attribute_value &other) const
~config_attribute_value()
Default implementation, but defined out-of-line for efficiency reasons.
friend std::ostream & operator<<(std::ostream &os, const yes_no &v)
static const std::string s_false