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