The Battle for Wesnoth  1.15.3+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  // This is to prevent int conversion being used when an attribute value is tested in an if statement
176  explicit operator bool() const {return to_bool(); }
177 
178  /// Tests for an attribute that was never set.
179  bool blank() const;
180  /// Tests for an attribute that either was never set or was set to "".
181  bool empty() const;
182 
183 
184  // Comparisons:
185  bool operator==(const config_attribute_value &other) const;
186  bool operator!=(const config_attribute_value &other) const
187  {
188  return !operator==(other);
189  }
190 
191  bool equals(const std::string& str) const;
192  // These function prevent t_string creation in case of c["a"] == "b" comparisons.
193  // The templates are needed to prevent using these function in case of c["a"] == 0 comparisons.
194  template<typename T>
195  std::enable_if_t<std::is_same<const std::string, std::add_const_t<T>>::value, bool>
196  friend operator==(const config_attribute_value &val, const T &str)
197  {
198  return val.equals(str);
199  }
200 
201  template<typename T>
202  std::enable_if_t<std::is_same<const char*, T>::value, bool>
203  friend operator==(const config_attribute_value& val, T str)
204  {
205  return val.equals(std::string(str));
206  }
207 
208  template<typename T>
209  bool friend operator==(const T& str, const config_attribute_value& val)
210  {
211  return val == str;
212  }
213 
214  template<typename T>
215  bool friend operator!=(const config_attribute_value& val, const T& str)
216  {
217  return !(val == str);
218  }
219 
220  template<typename T>
221  bool friend operator!=(const T &str, const config_attribute_value& val)
222  {
223  return !(val == str);
224  }
225 
226  // Streaming:
227  friend std::ostream& operator<<(std::ostream& os, const config_attribute_value& v);
228 
229  // Visitor support:
230  /// Applies a visitor to the underlying variant.
231  /// (See the documentation for Boost.Variant.)
232  template <typename V>
233  typename V::result_type apply_visitor(const V & visitor) const
234  {
235  return boost::apply_visitor(visitor, value_);
236  }
237 
238 private:
239  // Special strings.
240  static const std::string s_yes, s_no;
241  static const std::string s_true, s_false;
242 };
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