The Battle for Wesnoth  1.15.0+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 #ifdef HAVE_CXX14
51 # ifdef __clang__ // Check this first, because clang also defines __GNUC__
52 # ifdef __apple_build_version__ // Apple clang
53 # if (__clang_major__ == 5 && __clang_minor__ >= 1) || __clang_major__ > 5 // Apple clang 5.1+
54 # define USE_HETEROGENOUS_LOOKUPS
55 # endif
56 # else // Non-Apple clang
57 # if (__clang_major__ == 3 && __clang_minor__ >= 4) || __clang_major__ > 3 // clang 3.4+
58 # define USE_HETEROGENOUS_LOOKUPS
59 # endif
60 # endif
61 # elif defined(__GNUC__) && __GNUC__ >= 5 // GCC 5.0+
62 # define USE_HETEROGENOUS_LOOKUPS
63 # endif
64 #endif
65 
66 #if defined(_MSC_VER) && _MSC_VER >= 1900 // MSVC 2015
67 # define USE_HETEROGENOUS_LOOKUPS
68 #endif
69 
70 class enum_tag;
71 
72 /**
73  * Variant for storing WML attributes.
74  * The most efficient type is used when assigning a value. For instance,
75  * strings "yes", "no", "true", "false" will be detected and stored as boolean.
76  * @note The blank variant is only used when querying missing attributes.
77  * It is not stored in config objects.
78  */
80 {
81  /// A wrapper for bool to get the correct streaming ("true"/"false").
82  /// Most visitors can simply treat this as bool.
83 public:
84  class true_false
85  {
86  bool value_;
87  public:
88  explicit true_false(bool value = false) : value_(value) {}
89  operator bool() const { return value_; }
90 
91  const std::string & str() const
92  {
94  }
95  };
96  friend std::ostream& operator<<(std::ostream &os, const true_false &v) { return os << v.str(); }
97 
98  /// A wrapper for bool to get the correct streaming ("yes"/"no").
99  /// Most visitors can simply treat this as bool.
100  class yes_no
101  {
102  bool value_;
103  public:
104  explicit yes_no(bool value = false) : value_(value) {}
105  operator bool() const { return value_; }
106 
107  const std::string & str() const
108  {
110  }
111  };
112  friend std::ostream& operator<<(std::ostream &os, const yes_no &v) { return os << v.str(); }
113 private:
114  /// Visitor for checking equality.
115  class equality_visitor;
116  /// Visitor for converting a variant to a string.
118 
119  // Data will be stored in a variant, allowing for the possibility of
120  // boolean, numeric, and translatable data in addition to basic string
121  // data. For most purposes, int is the preferred type for numeric data
122  // as it is fast (often natural word size). While it is desirable to
123  // use few types (to keep the overhead low), we do have use cases for
124  // fractions (double) and huge numbers (up to the larger of LLONG_MAX
125  // and SIZE_MAX).
126  typedef boost::variant<boost::blank,
128  int, unsigned long long, double,
129  std::string, t_string
130  > value_type;
131  /// The stored value will always use the first type from the variant
132  /// definition that can represent it and that can be streamed to the
133  /// correct string representation (if applicable).
134  /// This is enforced upon assignment.
135  value_type value_;
136 
137 public:
138  /// Default implementation, but defined out-of-line for efficiency reasons.
140  /// Default implementation, but defined out-of-line for efficiency reasons.
142  /// Default implementation, but defined out-of-line for efficiency reasons.
144  /// Default implementation, but defined out-of-line for efficiency reasons.
146 
147  // Numeric assignments:
150  config_attribute_value& operator=(long v) { return operator=(static_cast<long long>(v)); }
151  config_attribute_value& operator=(long long v);
152  config_attribute_value& operator=(unsigned v) { return operator=(static_cast<unsigned long long>(v)); }
153  config_attribute_value& operator=(unsigned long v) { return operator=(static_cast<unsigned long long>(v)); }
154  config_attribute_value& operator=(unsigned long long v);
156 
157  // String assignments:
158  config_attribute_value& operator=(const char *v) { return operator=(std::string(v)); }
159  config_attribute_value& operator=(const std::string &v);
161  template<typename T>
162  std::enable_if_t<std::is_base_of<enum_tag, T>::value, config_attribute_value&> operator=(const T &v)
163  {
164  return operator=(T::enum_to_string(v));
165  }
166 
167  /** Calls @ref operator=(const std::string&) if @a v is not empty. */
168  void write_if_not_empty(const std::string& v);
169 
170  // Extracting as a specific type:
171  bool to_bool(bool def = false) const;
172  int to_int(int def = 0) const;
173  long long to_long_long(long long def = 0) const;
174  unsigned to_unsigned(unsigned def = 0) const;
175  std::size_t to_size_t(std::size_t def = 0) const;
176  std::time_t to_time_t(std::time_t def = 0) const;
177  double to_double(double def = 0.) const;
178  std::string str(const std::string& fallback = "") const;
179  t_string t_str() const;
180  /**
181  @tparam T a type created with MAKE_ENUM macro
182  NOTE: since T::VALUE constants is not of type T but of the underlying enum type you must specify the template parameter explicitly
183  TODO: Fix this in c++11 using constexpr types.
184  */
185  template<typename T>
186  std::enable_if_t<std::is_base_of<enum_tag, T>::value, T> to_enum(const T &v) const
187  {
188  return T::string_to_enum(this->str(), v);
189  }
190 
191  // Implicit conversions:
192  operator int() const { return to_int(); }
193  operator std::string() const { return str(); }
194  operator t_string() const { return t_str(); }
195 
196  /// Tests for an attribute that was never set.
197  bool blank() const;
198  /// Tests for an attribute that either was never set or was set to "".
199  bool empty() const;
200 
201 
202  // Comparisons:
203  bool operator==(const config_attribute_value &other) const;
204  bool operator!=(const config_attribute_value &other) const
205  {
206  return !operator==(other);
207  }
208 
209  bool equals(const std::string& str) const;
210  // These function prevent t_string creation in case of c["a"] == "b" comparisons.
211  // The templates are needed to prevent using these function in case of c["a"] == 0 comparisons.
212  template<typename T>
213  std::enable_if_t<std::is_same<const std::string, std::add_const_t<T>>::value, bool>
214  friend operator==(const config_attribute_value &val, const T &str)
215  {
216  return val.equals(str);
217  }
218 
219  template<typename T>
220  std::enable_if_t<std::is_same<const char*, T>::value, bool>
221  friend operator==(const config_attribute_value& val, T str)
222  {
223  return val.equals(std::string(str));
224  }
225 
226  template<typename T>
227  bool friend operator==(const T& str, const config_attribute_value& val)
228  {
229  return val == str;
230  }
231 
232  template<typename T>
233  bool friend operator!=(const config_attribute_value& val, const T& str)
234  {
235  return !(val == str);
236  }
237 
238  template<typename T>
239  bool friend operator!=(const T &str, const config_attribute_value& val)
240  {
241  return !(val == str);
242  }
243 
244  // Streaming:
245  friend std::ostream& operator<<(std::ostream& os, const config_attribute_value& v);
246 
247  // Visitor support:
248  /// Applies a visitor to the underlying variant.
249  /// (See the documentation for Boost.Variant.)
250  template <typename V>
251  typename V::result_type apply_visitor(const V & visitor) const
252  {
253  return boost::apply_visitor(visitor, value_);
254  }
255 
256 private:
257  // Special strings.
258  static const std::string s_yes, s_no;
259  static const std::string s_true, s_false;
260 };
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)
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