The Battle for Wesnoth  1.19.21+dev
typed_formula.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2025
3  by Mark de Wever <koraq@xs4all.nl>
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 #pragma once
17 
18 #include "color.hpp"
19 #include "formula/callable.hpp"
20 #include "formula/formula.hpp"
21 #include "formula/function.hpp"
22 #include "gui/core/log.hpp"
23 #include "gui/widgets/helper.hpp"
24 #include "lexical_cast.hpp"
26 #include "tstring.hpp"
27 
28 #include <cassert>
29 #include "utils/optional_fwd.hpp"
30 
31 namespace gui2
32 {
33 
34 /**
35  * Template class can hold a value or a formula to calculate the value.
36  *
37  * A string is a formula when it starts with a right paren, no other validation
38  * is done by this function, leading whitespace is significant.
39  *
40  * Upon getting the value of the formula a variable map is send. The variables
41  * in the map can be used in the formula. The 'owners' of the class need to
42  * document the variables available.
43  *
44  * @tparam T The type of the formula. This type needs to
45  * be constructable form a string, either by a
46  * lexical_cast or a template specialization in
47  * this header.
48  */
49 template<typename T>
51 {
52 public:
53  /**
54  * Constructor.
55  *
56  * @param str The string used to initialize the class, this
57  * can either be a formula or a string which can
58  * be converted to the type T.
59  * @param value The default value for the object.
60  */
61  explicit typed_formula(const std::string& str, const T value = T());
62 
63  explicit typed_formula(T value);
64 
65  /** Set the value for this object */
66  void set_value(T value) { value_ = value; }
67 
68  /**
69  * Returns the value, can only be used if the data is no formula.
70  *
71  * Another option would be to cache the output of the formula in value_
72  * and always allow this function. But for now decided that the caller
73  * needs to do the caching. It might be changed later.
74  */
75  T operator()() const
76  {
77  assert(!has_formula());
78  return value_;
79  }
80 
81  /**
82  * Returns the value, can always be used.
83  *
84  * @param variables The state variables which might be used in
85  * the formula. For example, screen_width can
86  * be set so the formula can return the half
87  * width of the screen.
88  *
89  * @param functions The variables, which can be called during the
90  * evaluation of the formula. (Note it is also
91  * possible to add extra functions to the table,
92  * when the variable is not @c nullptr.
93  *
94  * @returns The stored result or the result of the
95  * evaluation of the formula.
96  */
98  wfl::function_symbol_table* functions = nullptr) const;
99 
100  /** Determine whether the class contains a formula. */
101  bool has_formula() const
102  {
103  return formula_.has_value();
104  }
105 
106 private:
107  /**
108  * Converts the string to the template type.
109  *
110  * This function is used by the constructor to convert the string to the
111  * wanted value, if not a formula.
112  *
113  * @param str The str send to the constructor.
114  */
115  void convert(const std::string& str);
116 
117  /**
118  * Executes the formula.
119  *
120  * This function does the calculation and can only be called if the object
121  * contains a formula.
122  *
123  * @param v A variant object containing the evaluated value
124  * of the formula.
125  *
126  * @returns The calculated value.
127  */
128  T execute(wfl::variant& v) const;
129 
130  /**
131  * Contains the formula for the variable.
132  *
133  * If without value, there's no formula.
134  */
135  utils::optional<std::string> formula_;
136 
137  /** If there's no formula it contains the value. */
139 };
140 
141 template<typename T>
142 typed_formula<T>::typed_formula(const std::string& str, const T value)
143  : formula_(), value_(value)
144 {
145  if(str.empty()) {
146  return;
147  }
148 
149  if(str[0] == '(') {
150  formula_ = str;
151  } else {
152  convert(str);
153  }
154 }
155 
156 template<typename T>
158  : formula_(), value_(value)
159 {
160 }
161 
162 template<typename T>
164 operator()(const wfl::map_formula_callable& variables, wfl::function_symbol_table* functions) const
165 {
166  if(!has_formula()) {
167  return value_;
168  }
169 
170  wfl::variant v = wfl::formula(*formula_, functions).evaluate(variables);
171  const T& result = execute(v);
172 
173  DBG_GUI_D << "Formula: execute '" << *formula_ << "' result '" << result << "'.";
174 
175  return result;
176 }
177 
178 /**
179  * Template specializations.
180  *
181  * Each type must have an @ref execute specialization, and optionally one for @ref convert.
182  */
183 
184 template<>
186 {
187  return v.as_bool();
188 }
189 
190 template<>
191 inline void typed_formula<bool>::convert(const std::string& str)
192 {
193  value_ = utils::string_bool(str);
194 }
195 
196 
197 template<>
199 {
200  return v.as_int();
201 }
202 
203 
204 template<>
206 {
207  // FIXME: Validate this? As is, the formula could return a negative number which is blindly converted to unsigned.
208  // Unfortunately, some places rely on this happening for diagnostic messages...
209  return v.as_int();
210 }
211 
212 
213 template<>
215 {
216  return v.as_string();
217 }
218 
219 template<>
220 inline void typed_formula<std::string>::convert(const std::string& str)
221 {
222  value_ = str;
223 }
224 
225 
226 template<>
228 {
229  return v.as_string();
230 }
231 
232 template<>
233 inline void typed_formula<t_string>::convert(const std::string& str)
234 {
235  value_ = str;
236 }
237 
238 
239 template<>
241 {
242  return decode_text_alignment(v.as_string());
243 }
244 
245 template<>
246 inline void typed_formula<PangoAlignment>::convert(const std::string& str)
247 {
249 }
250 
251 
252 template<>
254 {
255  const auto& result = v.as_list();
256  const int alpha = result.size() == 4 ? result[3].as_int() : ALPHA_OPAQUE;
257 
258  return color_t(
259  result.at(0).as_int(),
260  result.at(1).as_int(),
261  result.at(2).as_int(),
262  alpha
263  );
264 }
265 
266 template<>
267 inline void typed_formula<color_t>::convert(const std::string& str)
268 {
270 }
271 
272 
273 template<typename T>
275 {
276  // Every type needs its own execute function avoid instantiation of the
277  // default execute.
278  static_assert(sizeof(T) == 0, "typed_formula: Missing execute specialization");
279  return T();
280 }
281 
282 template<class T>
283 inline void typed_formula<T>::convert(const std::string& str)
284 {
285  value_ = lexical_cast_default<T>(str);
286 }
287 
288 } // namespace gui2
std::unique_ptr< PangoAttribute, void(*)(PangoAttribute *)> value_
Definition: attributes.cpp:60
Template class can hold a value or a formula to calculate the value.
bool has_formula() const
Determine whether the class contains a formula.
typed_formula(const std::string &str, const T value=T())
Constructor.
utils::optional< std::string > formula_
Contains the formula for the variable.
void convert(const std::string &str)
Converts the string to the template type.
T value_
If there's no formula it contains the value.
T operator()() const
Returns the value, can only be used if the data is no formula.
T execute(wfl::variant &v) const
Executes the formula.
void set_value(T value)
Set the value for this object.
T operator()(const wfl::map_formula_callable &variables, wfl::function_symbol_table *functions=nullptr) const
Returns the value, can always be used.
static variant evaluate(const const_formula_ptr &f, const formula_callable &variables, formula_debugger *fdb=nullptr, variant default_res=variant(0))
Definition: formula.hpp:48
int as_int(int fallback=0) const
Returns the variant's value as an integer.
Definition: variant.cpp:300
const std::string & as_string() const
Definition: variant.cpp:327
bool as_bool() const
Returns a boolean state of the variant value.
Definition: variant.cpp:322
const std::vector< variant > & as_list() const
Definition: variant.cpp:333
constexpr uint8_t ALPHA_OPAQUE
Definition: color.hpp:37
Define the common log macros for the gui toolkit.
#define DBG_GUI_D
Definition: log.hpp:29
New lexcical_cast header.
Generic file dialog.
PangoAlignment decode_text_alignment(const std::string &alignment)
Converts a text alignment string to a text alignment.
Definition: helper.cpp:84
bool string_bool(const std::string &str, bool def)
Convert no, false, off, 0, 0.0 to false, empty to def, and others to true.
The basic class for representing 8-bit RGB or RGBA colour values.
Definition: color.hpp:51
static color_t from_rgba_string(std::string_view c)
Creates a new color_t object from a string variable in "R,G,B,A" format.
Definition: color.cpp:23