The Battle for Wesnoth  1.19.5+dev
typed_formula.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2024
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  /**
64  * Returns the value, can only be used if the data is no formula.
65  *
66  * Another option would be to cache the output of the formula in value_
67  * and always allow this function. But for now decided that the caller
68  * needs to do the caching. It might be changed later.
69  */
70  T operator()() const
71  {
72  assert(!has_formula());
73  return value_;
74  }
75 
76  /**
77  * Returns the value, can always be used.
78  *
79  * @param variables The state variables which might be used in
80  * the formula. For example, screen_width can
81  * be set so the formula can return the half
82  * width of the screen.
83  *
84  * @param functions The variables, which can be called during the
85  * evaluation of the formula. (Note it is also
86  * possible to add extra functions to the table,
87  * when the variable is not @c nullptr.
88  *
89  * @returns The stored result or the result of the
90  * evaluation of the formula.
91  */
93  wfl::function_symbol_table* functions = nullptr) const;
94 
95  /** Determine whether the class contains a formula. */
96  bool has_formula() const
97  {
98  return formula_.has_value();
99  }
100 
101 private:
102  /**
103  * Converts the string to the template type.
104  *
105  * This function is used by the constructor to convert the string to the
106  * wanted value, if not a formula.
107  *
108  * @param str The str send to the constructor.
109  */
110  void convert(const std::string& str);
111 
112  /**
113  * Executes the formula.
114  *
115  * This function does the calculation and can only be called if the object
116  * contains a formula.
117  *
118  * @param v A variant object containing the evaluated value
119  * of the formula.
120  *
121  * @returns The calculated value.
122  */
123  T execute(wfl::variant& v) const;
124 
125  /**
126  * Contains the formula for the variable.
127  *
128  * If without value, there's no formula.
129  */
130  utils::optional<std::string> formula_;
131 
132  /** If there's no formula it contains the value. */
134 };
135 
136 template<typename T>
137 typed_formula<T>::typed_formula(const std::string& str, const T value)
138  : formula_(), value_(value)
139 {
140  if(str.empty()) {
141  return;
142  }
143 
144  if(str[0] == '(') {
145  formula_ = str;
146  } else {
147  convert(str);
148  }
149 }
150 
151 template<typename T>
153 operator()(const wfl::map_formula_callable& variables, wfl::function_symbol_table* functions) const
154 {
155  if(!has_formula()) {
156  return value_;
157  }
158 
159  wfl::variant v = wfl::formula(*formula_, functions).evaluate(variables);
160  const T& result = execute(v);
161 
162  DBG_GUI_D << "Formula: execute '" << *formula_ << "' result '" << result << "'.";
163 
164  return result;
165 }
166 
167 /**
168  * Template specializations.
169  *
170  * Each type must have an @ref execute specialization, and optionally one for @ref convert.
171  */
172 
173 template<>
175 {
176  return v.as_bool();
177 }
178 
179 template<>
180 inline void typed_formula<bool>::convert(const std::string& str)
181 {
182  value_ = utils::string_bool(str);
183 }
184 
185 
186 template<>
188 {
189  return v.as_int();
190 }
191 
192 
193 template<>
195 {
196  // FIXME: Validate this? As is, the formula could return a negative number which is blindly converted to unsigned.
197  // Unfortunately, some places rely on this happening for diagnostic messages...
198  return v.as_int();
199 }
200 
201 
202 template<>
204 {
205  return v.as_string();
206 }
207 
208 template<>
209 inline void typed_formula<std::string>::convert(const std::string& str)
210 {
211  value_ = str;
212 }
213 
214 
215 template<>
217 {
218  return v.as_string();
219 }
220 
221 template<>
222 inline void typed_formula<t_string>::convert(const std::string& str)
223 {
224  value_ = str;
225 }
226 
227 
228 template<>
230 {
231  return decode_text_alignment(v.as_string());
232 }
233 
234 template<>
235 inline void typed_formula<PangoAlignment>::convert(const std::string& str)
236 {
237  value_ = decode_text_alignment(str);
238 }
239 
240 
241 template<>
243 {
244  const auto& result = v.as_list();
245  const int alpha = result.size() == 4 ? result[3].as_int() : ALPHA_OPAQUE;
246 
247  return color_t(
248  result.at(0).as_int(),
249  result.at(1).as_int(),
250  result.at(2).as_int(),
251  alpha
252  );
253 }
254 
255 template<>
256 inline void typed_formula<color_t>::convert(const std::string& str)
257 {
258  value_ = color_t::from_rgba_string(str);
259 }
260 
261 
262 template<typename T>
264 {
265  // Every type needs its own execute function avoid instantiation of the
266  // default execute.
267  static_assert(sizeof(T) == 0, "typed_formula: Missing execute specialization");
268  return T();
269 }
270 
271 template<class T>
272 inline void typed_formula<T>::convert(const std::string& str)
273 {
274  value_ = lexical_cast_default<T>(str);
275 }
276 
277 } // namespace gui2
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.
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:40
int as_int() const
Definition: variant.cpp:291
const std::string & as_string() const
Definition: variant.cpp:318
bool as_bool() const
Returns a boolean state of the variant value.
Definition: variant.cpp:313
const std::vector< variant > & as_list() const
Definition: variant.cpp:324
constexpr uint8_t ALPHA_OPAQUE
Definition: color.hpp:45
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:89
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:59
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