The Battle for Wesnoth  1.17.23+dev
config_attribute_value.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2023
3  by David White <dave@whitevine.net>
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  * Definitions for the interface to Wesnoth Markup Language (WML).
19  *
20  * This module defines the interface to Wesnoth Markup Language (WML). WML is
21  * a simple hierarchical text-based file format. The format is defined in
22  * Wiki, under BuildingScenariosWML
23  *
24  * All configuration files are stored in this format, and data is sent across
25  * the network in this format. It is thus used extensively throughout the
26  * game.
27  */
28 
29 #pragma once
30 
31 #include "tstring.hpp"
32 #include "utils/variant.hpp"
33 
34 #include <climits>
35 #include <ctime>
36 #include <iosfwd>
37 #include <iterator>
38 #include <map>
39 #include <string>
40 #include <utility>
41 #include <vector>
42 #include <type_traits>
43 #include <memory>
44 
45 /**
46  * Variant for storing WML attributes.
47  * The most efficient type is used when assigning a value. For instance,
48  * strings "yes", "no", "true", "false" will be detected and stored as boolean.
49  * @note The blank variant is only used when querying missing attributes.
50  * It is not stored in config objects.
51  */
53 {
54  /**
55  * A wrapper for bool to get the correct streaming ("true"/"false").
56  * Most visitors can simply treat this as bool.
57  */
58 public:
59  class true_false
60  {
61  bool value_;
62  public:
63  explicit true_false(bool value = false) : value_(value) {}
64  operator bool() const { return value_; }
65 
66  const std::string & str() const
67  {
69  }
70  };
71  friend std::ostream& operator<<(std::ostream &os, const true_false &v) { return os << v.str(); }
72 
73  /**
74  * A wrapper for bool to get the correct streaming ("yes"/"no").
75  * Most visitors can simply treat this as bool.
76  */
77  class yes_no
78  {
79  bool value_;
80  public:
81  explicit yes_no(bool value = false) : value_(value) {}
82  operator bool() const { return value_; }
83 
84  const std::string & str() const
85  {
87  }
88  };
89  friend std::ostream& operator<<(std::ostream &os, const yes_no &v) { return os << v.str(); }
90 private:
91  /** Visitor for checking equality. */
92  class equality_visitor;
93  /** Visitor for converting a variant to a string. */
94  class string_visitor;
95 
96  // Data will be stored in a variant, allowing for the possibility of
97  // boolean, numeric, and translatable data in addition to basic string
98  // data. For most purposes, int is the preferred type for numeric data
99  // as it is fast (often natural word size). While it is desirable to
100  // use few types (to keep the overhead low), we do have use cases for
101  // fractions (double) and huge numbers (up to the larger of LLONG_MAX
102  // and SIZE_MAX).
103  typedef utils::variant<utils::monostate,
104  true_false, yes_no,
105  int, unsigned long long, double,
106  std::string, t_string
108  /**
109  * The stored value will always use the first type from the variant
110  * definition that can represent it and that can be streamed to the
111  * correct string representation (if applicable).
112  * This is enforced upon assignment.
113  */
115 
116 public:
117  /** Default implementation, but defined out-of-line for efficiency reasons. */
119  /** Default implementation, but defined out-of-line for efficiency reasons. */
121  /** Default implementation, but defined out-of-line for efficiency reasons. */
123  /** Default implementation, but defined out-of-line for efficiency reasons. */
125 
126  // Numeric assignments:
129  config_attribute_value& operator=(long v) { return operator=(static_cast<long long>(v)); }
130  config_attribute_value& operator=(long long v);
131  config_attribute_value& operator=(unsigned v) { return operator=(static_cast<unsigned long long>(v)); }
132  config_attribute_value& operator=(unsigned long v) { return operator=(static_cast<unsigned long long>(v)); }
133  config_attribute_value& operator=(unsigned long long v);
135 
136  // String assignments:
137  config_attribute_value& operator=(const char *v) { return operator=(std::string(v)); }
138  config_attribute_value& operator=(const std::string &v);
139  config_attribute_value& operator=(const std::string_view &v);
141 
142  /** Calls @ref operator=(const std::string&) if @a v is not empty. */
143  void write_if_not_empty(const std::string& v);
144 
145  // Extracting as a specific type:
146  bool to_bool(bool def = false) const;
147  int to_int(int def = 0) const;
148  long long to_long_long(long long def = 0) const;
149  unsigned to_unsigned(unsigned def = 0) const;
150  std::size_t to_size_t(std::size_t def = 0) const;
151  std::time_t to_time_t(std::time_t def = 0) const;
152  double to_double(double def = 0.) const;
153  std::string str(const std::string& fallback = "") const;
154  t_string t_str() const;
155 
156  // Implicit conversions:
157  operator int() const { return to_int(); }
158  operator std::string() const { return str(); }
159  operator t_string() const { return t_str(); }
160  // This is to prevent int conversion being used when an attribute value is tested in an if statement
161  explicit operator bool() const {return to_bool(); }
162 
163  /** Tests for an attribute that was never set. */
164  bool blank() const;
165  /** Tests for an attribute that either was never set or was set to "". */
166  bool empty() const;
167 
168 
169  // Comparisons:
170  bool operator==(const config_attribute_value &other) const;
171  bool operator!=(const config_attribute_value &other) const
172  {
173  return !operator==(other);
174  }
175 
176  bool equals(const std::string& str) const;
177  // These function prevent t_string creation in case of c["a"] == "b" comparisons.
178  // The templates are needed to prevent using these function in case of c["a"] == 0 comparisons.
179  template<typename T>
180  std::enable_if_t<std::is_same_v<const std::string, std::add_const_t<T>>, bool>
181  friend operator==(const config_attribute_value &val, const T &str)
182  {
183  return val.equals(str);
184  }
185 
186  template<typename T>
187  std::enable_if_t<std::is_same_v<const char*, T>, bool>
188  friend operator==(const config_attribute_value& val, T str)
189  {
190  return val.equals(std::string(str));
191  }
192 
193  template<typename T>
194  bool friend operator==(const T& str, const config_attribute_value& val)
195  {
196  return val == str;
197  }
198 
199  template<typename T>
200  bool friend operator!=(const config_attribute_value& val, const T& str)
201  {
202  return !(val == str);
203  }
204 
205  template<typename T>
206  bool friend operator!=(const T &str, const config_attribute_value& val)
207  {
208  return !(val == str);
209  }
210 
211  // Streaming:
212  friend std::ostream& operator<<(std::ostream& os, const config_attribute_value& v);
213 
214  /**
215  * Visitor support:
216  * Applies a visitor to the underlying variant.
217  * (See the documentation for Boost.Variant.)
218  */
219  template <typename V>
220  auto apply_visitor(const V & visitor) const
221  {
222  return utils::visit(visitor, value_);
223  }
224 
225 private:
226  // Special strings.
227  static const std::string s_yes, s_no;
228  static const std::string s_true, s_false;
229 };
230 
231 #ifndef USING_BOOST_VARIANT
232 /** Specialize operator<< for monostate. Boost already does this, but the STL does not. */
233 inline std::ostream& operator<<(std::ostream& os, const std::monostate&) { return os; }
234 #endif
235 
236 namespace utils
237 {
238  std::vector<std::string> split(const config_attribute_value& val);
239 }
A wrapper for bool to get the correct streaming ("true"/"false").
A wrapper for bool to get the correct streaming ("yes"/"no").
const std::string & str() const
Variant for storing WML attributes.
config_attribute_value & operator=(const config_attribute_value &)
Default implementation, but defined out-of-line for efficiency reasons.
bool equals(const std::string &str) const
Checks for equality of the attribute values when viewed as strings.
friend std::ostream & operator<<(std::ostream &os, const true_false &v)
static const std::string s_false
value_type value_
The stored value will always use the first type from the variant definition that can represent it and...
config_attribute_value & operator=(unsigned v)
std::string str(const std::string &fallback="") const
std::time_t to_time_t(std::time_t def=0) const
static const std::string s_true
auto apply_visitor(const V &visitor) const
Visitor support: Applies a visitor to the underlying variant.
unsigned to_unsigned(unsigned def=0) const
static const std::string s_yes
std::enable_if_t< std::is_same_v< const char *, T >, bool > friend operator==(const config_attribute_value &val, T str)
bool blank() const
Tests for an attribute that was never set.
static const std::string s_no
config_attribute_value & operator=(long v)
config_attribute_value & operator=(unsigned long v)
config_attribute_value()
Default implementation, but defined out-of-line for efficiency reasons.
std::enable_if_t< std::is_same_v< const std::string, std::add_const_t< T > >, bool > friend operator==(const config_attribute_value &val, const T &str)
bool friend operator!=(const config_attribute_value &val, const T &str)
bool operator!=(const config_attribute_value &other) const
bool to_bool(bool def=false) const
bool friend operator!=(const T &str, const config_attribute_value &val)
config_attribute_value & operator=(const char *v)
~config_attribute_value()
Default implementation, but defined out-of-line for efficiency reasons.
utils::variant< utils::monostate, true_false, yes_no, int, unsigned long long, double, std::string, t_string > value_type
bool friend operator==(const T &str, const config_attribute_value &val)
bool operator==(const config_attribute_value &other) const
Checks for equality of the attribute values when viewed as strings.
void write_if_not_empty(const std::string &v)
Calls operator=(const std::string&) if v is not empty.
bool empty() const
Tests for an attribute that either was never set or was set to "".
friend std::ostream & operator<<(std::ostream &os, const yes_no &v)
long long to_long_long(long long def=0) const
std::size_t to_size_t(std::size_t def=0) const
double to_double(double def=0.) const
std::ostream & operator<<(std::ostream &os, const std::monostate &)
Specialize operator<< for monostate.
std::vector< std::string > split(const config_attribute_value &val)
MacOS doesn't support std::visit when targing MacOS < 10.14 (currently we target 10....