The Battle for Wesnoth  1.13.10+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
typed_formula.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2017 by Mark de Wever <koraq@xs4all.nl>
3  Part of the Battle for Wesnoth Project http://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 
29 namespace gui2
30 {
31 
32 /**
33  * Template class can hold a value or a formula to calculate the value.
34  *
35  * A string is a formula when it starts with a right paren, no other validation
36  * is done by this function, leading whitespace is significant.
37  *
38  * Upon getting the value of the formula a variable map is send. The variables
39  * in the map can be used in the formula. The 'owners' of the class need to
40  * document the variables available.
41  *
42  * @tparam T The type of the formula. This type needs to
43  * be constructable form a string, either by a
44  * lexical_cast or a template specialization in
45  * this header.
46  */
47 template<typename T>
49 {
50 public:
51  /**
52  * Constructor.
53  *
54  * @param str The string used to initialize the class, this
55  * can either be a formula or a string which can
56  * be converted to the type T.
57  * @param value The default value for the object.
58  */
59  explicit typed_formula<T>(const std::string& str, const T value = T());
60 
61  /**
62  * Returns the value, can only be used if the data is no formula.
63  *
64  * Another option would be to cache the output of the formula in value_
65  * and always allow this function. But for now decided that the caller
66  * needs to do the caching. It might be changed later.
67  */
68  T operator()() const
69  {
70  assert(!has_formula());
71  return value_;
72  }
73 
74  /**
75  * Returns the value, can always be used.
76  *
77  * @param variables The state variables which might be used in
78  * the formula. For example, screen_width can
79  * be set so the formula can return the half
80  * width of the screen.
81  *
82  * @param functions The variables, which can be called during the
83  * evaluation of the formula. (Note it is also
84  * possible to add extra functions to the table,
85  * when the variable is not @c nullptr.
86  *
87  * @returns The stored result or the result of the
88  * evaluation of the formula.
89  */
90  T operator()(const wfl::map_formula_callable& variables,
91  wfl::function_symbol_table* functions = nullptr) const;
92 
93  /** Determine whether the class contains a formula. */
94  bool has_formula() const
95  {
96  return !formula_.empty();
97  }
98 
99 private:
100  /**
101  * Converts the string to the template type.
102  *
103  * This function is used by the constructor to convert the string to the
104  * wanted value, if not a formula.
105  *
106  * @param str The str send to the constructor.
107  */
108  void convert(const std::string& str);
109 
110  /**
111  * Executes the formula.
112  *
113  * This function does the calculation and can only be called if the object
114  * contains a formula.
115  *
116  * @param v A variant object containing the evaluated value
117  * of the formula.
118  *
119  * @returns The calculated value.
120  */
121  T execute(wfl::variant& v) const;
122 
123  /**
124  * Contains the formula for the variable.
125  *
126  * If the string is empty, there's no formula.
127  */
129 
130  /** If there's no formula it contains the value. */
132 };
133 
134 template<typename T>
136  : formula_(), value_(value)
137 {
138  if(str.empty()) {
139  return;
140  }
141 
142  if(str[0] == '(') {
143  formula_ = str;
144  } else {
145  convert(str);
146  }
147 }
148 
149 template<typename T>
150 inline T typed_formula<T>::
152 {
153  if(!has_formula()) {
154  return value_;
155  }
156 
157  wfl::variant v = wfl::formula(formula_, functions).evaluate(variables);
158  const T& result = execute(v);
159 
160  LOG_GUI_D << "Formula: execute '" << formula_ << "' result '" << result << "'.\n";
161 
162  return result;
163 }
164 
165 /**
166  * Template specializations.
167  *
168  * Each type must have an @ref execute specialization, and optionally one for @ref convert.
169  */
170 
171 template<>
173 {
174  return v.as_bool();
175 }
176 
177 template<>
179 {
180  value_ = utils::string_bool(str);
181 }
182 
183 
184 template<>
186 {
187  return v.as_int();
188 }
189 
190 
191 template<>
193 {
194  // FIXME: Validate this? As is, the formula could return a negative number which is blindly converted to unsigned.
195  // Unfortunately, some places rely on this happening for diagnostic messages...
196  return v.as_int();
197 }
198 
199 
200 template<>
202 {
203  return v.as_string();
204 }
205 
206 template<>
208 {
209  value_ = str;
210 }
211 
212 
213 template<>
215 {
216  return v.as_string();
217 }
218 
219 template<>
221 {
222  value_ = str;
223 }
224 
225 
226 template<>
228 {
229  return decode_text_alignment(v.as_string());
230 }
231 
232 template<>
234 {
235  value_ = decode_text_alignment(str);
236 }
237 
238 
239 template<>
241 {
242  const auto& result = v.as_list();
243  const int alpha = result.size() == 4 ? result[3].as_int() : ALPHA_OPAQUE;
244 
245  return color_t(
246  result.at(0).as_int(),
247  result.at(1).as_int(),
248  result.at(2).as_int(),
249  alpha
250  );
251 }
252 
253 template<>
255 {
256  value_ = color_t::from_rgba_string(str);
257 }
258 
259 
260 template<typename T>
262 {
263  // Every type needs its own execute function avoid instantiation of the
264  // default execute.
265  static_assert(sizeof(T) == 0, "typed_formula: Missing execute specialization");
266  return T();
267 }
268 
269 template<class T>
270 inline void typed_formula<T>::convert(const std::string& str)
271 {
272  value_ = lexical_cast_default<T>(str);
273 }
274 
275 } // namespace gui2
Define the common log macros for the gui toolkit.
std::vector< char_t > string
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
std::string formula_
Contains the formula for the variable.
T value_
If there's no formula it contains the value.
New lexcical_cast header.
int as_int() const
Definition: variant.cpp:298
T operator()() const
Returns the value, can only be used if the data is no formula.
const std::vector< variant > & as_list() const
Definition: variant.cpp:331
#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:78
Generic file dialog.
Definition: text.hpp:37
bool as_bool() const
Returns a boolean state of the variant value.
Definition: variant.cpp:320
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
const std::string & as_string() const
Definition: variant.cpp:325
Template class can hold a value or a formula to calculate the value.
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