The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
config_attribute_value.hpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2003 - 2025
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#ifdef __cpp_concepts
36#include <concepts>
37#endif
38#include <iosfwd>
39#include <string>
40#include <type_traits>
41
42#ifdef __cpp_concepts
43template<typename T>
44concept StringLike = std::constructible_from<std::string, T>;
45#endif
46
47/**
48 * Variant for storing WML attributes.
49 * The most efficient type is used when assigning a value. For instance,
50 * strings "yes", "no", "true", "false" will be detected and stored as boolean.
51 * @note The blank variant is only used when querying missing attributes.
52 * It is not stored in config objects.
53 */
55{
56 /**
57 * A wrapper for bool to get the correct streaming ("true"/"false").
58 * Most visitors can simply treat this as bool.
59 */
60public:
62 {
63 bool value_;
64 public:
65 explicit true_false(bool value = false) : value_(value) {}
66 operator bool() const { return value_; }
67
68 const std::string & str() const
69 {
71 }
72 };
73 friend std::ostream& operator<<(std::ostream &os, const true_false &v) { return os << v.str(); }
74
75 /**
76 * A wrapper for bool to get the correct streaming ("yes"/"no").
77 * Most visitors can simply treat this as bool.
78 */
79 class yes_no
80 {
81 bool value_;
82 public:
83 explicit yes_no(bool value = false) : value_(value) {}
84 operator bool() const { return value_; }
85
86 const std::string & str() const
87 {
89 }
90 };
91 friend std::ostream& operator<<(std::ostream &os, const yes_no &v) { return os << v.str(); }
92private:
93 /** Visitor for checking equality. */
94 class equality_visitor;
95 /** Visitor for converting a variant to a string. */
96 class string_visitor;
97
98 // Data will be stored in a variant, allowing for the possibility of
99 // boolean, numeric, and translatable data in addition to basic string
100 // data. For most purposes, int is the preferred type for numeric data
101 // as it is fast (often natural word size). While it is desirable to
102 // use few types (to keep the overhead low), we do have use cases for
103 // fractions (double) and huge numbers (up to the larger of LLONG_MAX
104 // and SIZE_MAX).
105 typedef utils::variant<utils::monostate,
106 true_false, yes_no,
107 int, unsigned long long, double,
108 std::string, t_string
110 /**
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 */
117
118public:
119 // Numeric assignments:
122 config_attribute_value& operator=(long v) { return operator=(static_cast<long long>(v)); }
123 config_attribute_value& operator=(long long v);
124 config_attribute_value& operator=(unsigned v) { return operator=(static_cast<unsigned long long>(v)); }
125 config_attribute_value& operator=(unsigned long v) { return operator=(static_cast<unsigned long long>(v)); }
126 config_attribute_value& operator=(unsigned long long v);
128
129 // String assignments:
130 config_attribute_value& operator=(const char *v) { return operator=(std::string(v)); }
131 config_attribute_value& operator=(std::string&& v);
132 config_attribute_value& operator=(const std::string &v);
133 config_attribute_value& operator=(const std::string_view &v);
135
136 //TODO: should this be a normal constructor?
137 template<typename T>
138 static config_attribute_value create(const T val)
139 {
141 res = val;
142 return res;
143 }
144
145 template<typename... Args>
146 config_attribute_value& operator=(const std::chrono::duration<Args...>& v)
147 {
148 return this->operator=(v.count());
149 }
150
151 /** Calls @ref operator=(const std::string&) if @a v is not empty. */
152 void write_if_not_empty(const std::string& v);
153 void write_if_not_empty(const t_string& v);
154
155 // Extracting as a specific type:
156 bool to_bool(bool def = false) const;
157 int to_int(int def = 0) const;
158 long long to_long_long(long long def = 0) const;
159 unsigned to_unsigned(unsigned def = 0) const;
160 std::size_t to_size_t(std::size_t def = 0) const;
161 double to_double(double def = 0.) const;
162 std::string str(const std::string& fallback = "") const;
163 t_string t_str() const;
164
165 bool to(const bool def) const { return to_bool(def); }
166 int to(int def) const { return to_int(def); }
167 unsigned to(unsigned def) const { return to_unsigned(def); }
168 double to(double def) const { return to_double(def); }
169 std::string to(const std::string& def) const { return str(def); }
170
171 // Implicit conversions:
172 operator std::string() const { return str(); }
173
174 /** Tests for an attribute that was never set. */
175 bool blank() const;
176 /** Tests for an attribute that either was never set or was set to "". */
177 bool empty() const;
178
179 // Comparisons:
180 bool operator==(const config_attribute_value &other) const;
181 bool operator!=(const config_attribute_value &other) const
182 {
183 return !operator==(other);
184 }
185
186#ifdef __cpp_concepts
187 bool operator==(const StringLike auto& comp) const
188 {
189 return apply_visitor([this, &comp]<typename V>(const V& value) {
190 if constexpr(StringLike<V>) {
191 return value == comp;
192 } else {
193 return *this == create(comp);
194 }
195 });
196 }
197#else
198 template<typename T>
199 std::enable_if_t<std::is_constructible_v<std::string, T>, bool>
200 friend operator==(const config_attribute_value& attribute, const T& comp)
201 {
202 return attribute.apply_visitor([&](const auto& value) {
203 if constexpr(std::is_constructible_v<std::string, std::decay_t<decltype(value)>>) {
204 return value == comp;
205 } else {
206 return attribute == config_attribute_value::create(comp);
207 }
208 });
209 }
210
211 template<typename T>
212 std::enable_if_t<std::is_constructible_v<std::string, T>, bool>
213 friend operator==(const T& comp, const config_attribute_value& val)
214 {
215 return val == comp;
216 }
217
218 template<typename T>
219 std::enable_if_t<std::is_constructible_v<std::string, T>, bool>
220 friend operator!=(const config_attribute_value& val, const T& comp)
221 {
222 return !(val == comp);
223 }
224
225 template<typename T>
226 std::enable_if_t<std::is_constructible_v<std::string, T>, bool>
227 friend operator!=(const T& comp, const config_attribute_value& val)
228 {
229 return !(val == comp);
230 }
231#endif
232
233 // Streaming:
234 friend std::ostream& operator<<(std::ostream& os, const config_attribute_value& v);
235
236 /**
237 * Visitor support:
238 * Applies a visitor to the underlying variant.
239 * (See the documentation for Boost.Variant.)
240 */
241 template <typename V>
242 auto apply_visitor(const V & visitor) const
243 {
244 return utils::visit(visitor, value_);
245 }
246
247private:
248 // Special strings.
249 static const std::string s_yes, s_no;
250 static const std::string s_true, s_false;
251};
252
253#ifndef USING_BOOST_VARIANT
254/** Specialize operator<< for monostate. Boost already does this, but the STL does not. */
255inline std::ostream& operator<<(std::ostream& os, const std::monostate&) { return os; }
256#endif
257
258namespace utils
259{
260 std::vector<std::string> split(const config_attribute_value& val);
261}
A wrapper for bool to get the correct streaming ("true"/"false").
A wrapper for bool to get the correct streaming ("yes"/"no").
Variant for storing WML attributes.
config_attribute_value & operator=(long v)
static const std::string s_false
config_attribute_value & operator=(const char *v)
value_type value_
The stored value will always use the first type from the variant definition that can represent it and...
std::string str(const std::string &fallback="") 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.
std::enable_if_t< std::is_constructible_v< std::string, T >, bool > friend operator!=(const T &comp, const config_attribute_value &val)
unsigned to_unsigned(unsigned def=0) const
static const std::string s_yes
config_attribute_value & operator=(unsigned v)
unsigned to(unsigned def) const
bool blank() const
Tests for an attribute that was never set.
friend std::ostream & operator<<(std::ostream &os, const true_false &v)
static const std::string s_no
std::enable_if_t< std::is_constructible_v< std::string, T >, bool > friend operator==(const config_attribute_value &attribute, const T &comp)
bool to(const bool def) const
config_attribute_value & operator=(unsigned long v)
bool operator!=(const config_attribute_value &other) const
bool to_bool(bool def=false) const
config_attribute_value & operator=(bool v)
friend std::ostream & operator<<(std::ostream &os, const yes_no &v)
static config_attribute_value create(const T val)
utils::variant< utils::monostate, true_false, yes_no, int, unsigned long long, double, std::string, t_string > value_type
std::enable_if_t< std::is_constructible_v< std::string, T >, bool > friend operator==(const T &comp, const config_attribute_value &val)
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)
std::enable_if_t< std::is_constructible_v< std::string, T >, bool > friend operator!=(const config_attribute_value &val, const T &comp)
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)
std::unique_ptr< MIX_Audio, decltype(&MIX_DestroyAudio)> value
Definition sound.cpp:139
MacOS doesn't support std::visit when targing MacOS < 10.14 (currently we target 10....