The Battle for Wesnoth  1.19.7+dev
config_attribute_value.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2024
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 <chrono>
35 #include <climits>
36 #include <ctime>
37 #include <iosfwd>
38 #include <string>
39 #include <vector>
40 #include <type_traits>
41 
42 /**
43  * Variant for storing WML attributes.
44  * The most efficient type is used when assigning a value. For instance,
45  * strings "yes", "no", "true", "false" will be detected and stored as boolean.
46  * @note The blank variant is only used when querying missing attributes.
47  * It is not stored in config objects.
48  */
50 {
51  /**
52  * A wrapper for bool to get the correct streaming ("true"/"false").
53  * Most visitors can simply treat this as bool.
54  */
55 public:
56  class true_false
57  {
58  bool value_;
59  public:
60  explicit true_false(bool value = false) : value_(value) {}
61  operator bool() const { return value_; }
62 
63  const std::string & str() const
64  {
66  }
67  };
68  friend std::ostream& operator<<(std::ostream &os, const true_false &v) { return os << v.str(); }
69 
70  /**
71  * A wrapper for bool to get the correct streaming ("yes"/"no").
72  * Most visitors can simply treat this as bool.
73  */
74  class yes_no
75  {
76  bool value_;
77  public:
78  explicit yes_no(bool value = false) : value_(value) {}
79  operator bool() const { return value_; }
80 
81  const std::string & str() const
82  {
84  }
85  };
86  friend std::ostream& operator<<(std::ostream &os, const yes_no &v) { return os << v.str(); }
87 private:
88  /** Visitor for checking equality. */
89  class equality_visitor;
90  /** Visitor for converting a variant to a string. */
91  class string_visitor;
92 
93  // Data will be stored in a variant, allowing for the possibility of
94  // boolean, numeric, and translatable data in addition to basic string
95  // data. For most purposes, int is the preferred type for numeric data
96  // as it is fast (often natural word size). While it is desirable to
97  // use few types (to keep the overhead low), we do have use cases for
98  // fractions (double) and huge numbers (up to the larger of LLONG_MAX
99  // and SIZE_MAX).
100  typedef utils::variant<utils::monostate,
101  true_false, yes_no,
102  int, unsigned long long, double,
103  std::string, t_string
105  /**
106  * The stored value will always use the first type from the variant
107  * definition that can represent it and that can be streamed to the
108  * correct string representation (if applicable).
109  * This is enforced upon assignment.
110  */
112 
113 public:
114  // Numeric assignments:
117  config_attribute_value& operator=(long v) { return operator=(static_cast<long long>(v)); }
118  config_attribute_value& operator=(long long v);
119  config_attribute_value& operator=(unsigned v) { return operator=(static_cast<unsigned long long>(v)); }
120  config_attribute_value& operator=(unsigned long v) { return operator=(static_cast<unsigned long long>(v)); }
121  config_attribute_value& operator=(unsigned long long v);
123 
124  // String assignments:
125  config_attribute_value& operator=(const char *v) { return operator=(std::string(v)); }
126  config_attribute_value& operator=(std::string&& v);
127  config_attribute_value& operator=(const std::string &v);
128  config_attribute_value& operator=(const std::string_view &v);
130 
131  //TODO: should this be a normal constructor?
132  template<typename T>
133  static config_attribute_value create(const T val)
134  {
136  res = val;
137  return res;
138  }
139 
140  template<typename... Args>
141  config_attribute_value& operator=(const std::chrono::duration<Args...>& v)
142  {
143  return this->operator=(v.count());
144  }
145 
146  /** Calls @ref operator=(const std::string&) if @a v is not empty. */
147  void write_if_not_empty(const std::string& v);
148  void write_if_not_empty(const t_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  bool to(const bool def) const { return to_bool(def); }
162  int to(int def) const { return to_int(def); }
163  unsigned to(unsigned def) const { return to_unsigned(def); }
164  double to(double def) const { return to_double(def); }
165  std::string to(const std::string& def) const { return str(def); }
166 
167  // Implicit conversions:
168  operator std::string() const { return str(); }
169  operator t_string() const { return t_str(); }
170 
171  /** Tests for an attribute that was never set. */
172  bool blank() const;
173  /** Tests for an attribute that either was never set or was set to "". */
174  bool empty() const;
175 
176  // Comparisons:
177  bool operator==(const config_attribute_value &other) const;
178  bool operator!=(const config_attribute_value &other) const
179  {
180  return !operator==(other);
181  }
182 
183  bool operator==(bool comp) const
184  {
185  const bool has_bool =
186  utils::holds_alternative<yes_no>(value_) ||
187  utils::holds_alternative<true_false>(value_);
188  return has_bool && to_bool() == comp;
189  }
190 
191  template<typename T>
192  bool operator==(const T& comp) const
193  {
194  if constexpr(std::is_convertible_v<T, std::string>) {
196  v = comp;
197  return *this == v;
198  } else {
199  return utils::holds_alternative<T>(value_) && this->to(T{}) == comp;
200  }
201  }
202 
203  template<typename T>
204  bool friend operator!=(const config_attribute_value& val, const T& str)
205  {
206  return !val.operator==(str);
207  }
208 
209  template<typename T>
210  bool friend operator!=(const T &str, const config_attribute_value& val)
211  {
212  return !val.operator==(str);
213  }
214 
215  // Streaming:
216  friend std::ostream& operator<<(std::ostream& os, const config_attribute_value& v);
217 
218  /**
219  * Visitor support:
220  * Applies a visitor to the underlying variant.
221  * (See the documentation for Boost.Variant.)
222  */
223  template <typename V>
224  auto apply_visitor(const V & visitor) const
225  {
226  return utils::visit(visitor, value_);
227  }
228 
229 private:
230  // Special strings.
231  static const std::string s_yes, s_no;
232  static const std::string s_true, s_false;
233 };
234 
235 #ifndef USING_BOOST_VARIANT
236 /** Specialize operator<< for monostate. Boost already does this, but the STL does not. */
237 inline std::ostream& operator<<(std::ostream& os, const std::monostate&) { return os; }
238 #endif
239 
240 namespace utils
241 {
242  std::vector<std::string> split(const config_attribute_value& val);
243 }
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.
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...
bool operator==(bool comp) const
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
std::string to(const std::string &def) 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
unsigned to(unsigned def) const
bool blank() const
Tests for an attribute that was never set.
static const std::string s_no
bool operator==(const T &comp) const
config_attribute_value & operator=(long v)
config_attribute_value & operator=(unsigned long v)
bool to(const bool def) const
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=(bool v)
static config_attribute_value create(const T val)
config_attribute_value & operator=(const char *v)
utils::variant< utils::monostate, true_false, yes_no, int, unsigned long long, double, std::string, t_string > value_type
double to(double def) const
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 "".
config_attribute_value & operator=(const std::chrono::duration< Args... > &v)
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....