The Battle for Wesnoth  1.19.19+dev
variant_value.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2017 - 2025
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 "exceptions.hpp"
18 #include "formula/callable_fwd.hpp"
19 #include "formula_variant.hpp"
20 #include "utils/any.hpp"
21 #include "utils/general.hpp"
22 
23 #include <functional>
24 #include <map>
25 #include <vector>
26 #include <boost/range/iterator_range.hpp>
27 
28 namespace wfl
29 {
30 class variant_value_base;
31 class variant_iterator;
32 class variant;
33 
34 using variant_vector = std::vector<variant>;
35 using variant_map_raw = std::map<variant, variant>;
36 using value_base_ptr = std::shared_ptr<variant_value_base>;
37 
38 struct type_error : public game::error
39 {
40  explicit type_error(const std::string& str);
41 };
42 
43 /** Casts a @ref variant_value_base shared pointer to a new derived type. */
44 template<typename T>
45 static std::shared_ptr<T> value_cast(value_base_ptr ptr)
46 {
47  std::shared_ptr<T> res = std::dynamic_pointer_cast<T>(ptr);
48  if(!res) {
49  throw type_error("Could not cast type");
50  }
51 
52  return res;
53 }
54 
55 /** Casts a @ref variant_value_base reference to a new derived type. */
56 template<typename T>
58 {
59  try {
60  return dynamic_cast<T&>(ptr);
61  } catch(const std::bad_cast&) {
62  throw type_error("Could not cast type");
63  }
64 }
65 
66 /**
67  * Base class for all variant types.
68  *
69  * This provides a common interface for all type classes to implement, as well as
70  * giving variant a base pointer type for its value member. It also serves as the
71  * implementation of the 'null' variant value.
72  *
73  * Do note this class should *not* implement any data members.
74  */
76 {
77 public:
78  /** Returns the number of elements in a type. Not relevant for every derivative. */
79  virtual std::size_t num_elements() const
80  {
81  return 0;
82  }
83 
84  /** Whether the stored value is considered empty or not. */
85  virtual bool is_empty() const
86  {
87  return true;
88  }
89 
90  /** Returns the stored variant value in plain string form. */
91  virtual std::string string_cast() const
92  {
93  return "0";
94  }
95 
96  /** Returns the stored variant value in formula syntax. */
97  virtual std::string get_serialized_string() const
98  {
99  return "null()";
100  }
101 
102  /** Returns debug info for the variant value. */
103  virtual std::string get_debug_string(formula_seen_stack& /*seen*/, bool /*verbose*/) const
104  {
105  return get_serialized_string();
106  }
107 
108  /** Returns a bool expression of the variant value. */
109  virtual bool as_bool() const
110  {
111  return false;
112  }
113 
114  /**
115  * Called to determine if this variant is equal to another _of the same type_.
116  * This function is _only_ called if get_type() returns the same result for both arguments.
117  */
118  virtual bool equals(variant_value_base& /*other*/) const
119  {
120  return true; // null is equal to null
121  }
122 
123  /**
124  * Called to determine if this variant is less than another _of the same type_.
125  * This function is _only_ called if get_type() returns the same result for both arguments.
126  */
127  virtual bool less_than(variant_value_base& /*other*/) const
128  {
129  return false; // null is not less than null
130  }
131 
132  /** Returns the id of the variant type */
134  {
135  return formula_variant::type::null;
136  }
137 
138  /**
139  * Creates an iterator pair that can be used for iteration.
140  * For an iterable type, it should use the two-argument constructor of variant-iterator,
141  * passing the underlying iterator as the utils::any parameter.
142  *
143  * This creates both the begin and end iterator, but the variant implementation
144  * discards one of the two.
145  */
146  virtual boost::iterator_range<variant_iterator> make_iterator() const;
147 
148  /**
149  * Implements the dereference functionality of variant_iterator
150  * for a value of this type.
151  *
152  * @param iter The opaque reference that was passed to the variant_iterator by @ref make_iterator.
153  */
154  virtual variant deref_iterator(const utils::any& iter) const;
155 
156  /**
157  * Implements the increment functionality of variant_iterator
158  * for a value of this type.
159  *
160  * The parameter is an opaque reference that was passed to the variant_iterator by @ref make_iterator.
161  */
162  virtual void iterator_inc(utils::any&) const {}
163 
164  /**
165  * Implements the decrement functionality of variant_iterator
166  * for a value of this type.
167  *
168  * The parameter is an opaque reference that was passed to the variant_iterator by @ref make_iterator.
169  */
170  virtual void iterator_dec(utils::any&) const {}
171 
172  /**
173  * Implements the equality functionality of variant_iterator
174  * for a value of this type.
175  *
176  * Note that this is only called if the two iterators are already known to be of the same type.
177  *
178  * The first parameter is an opaque reference that was passed to the variant_iterator by @ref make_iterator.
179  * The second parameter is an opaque reference that was passed to the variant_iterator by @ref make_iterator.
180  */
181  virtual bool iterator_equals(const utils::any& /*first*/, const utils::any& /*second*/) const
182  {
183  return true;
184  }
185 
186  virtual ~variant_value_base() {}
187 };
188 
189 
190 /**
191  * Base class for numeric variant values. Currently only supports a value stored as an
192  * integer, but for now, that's all that's necessary.
193  */
195 {
196 public:
197  explicit variant_numeric(int value) : value_(value) {}
198 
199  virtual bool as_bool() const override
200  {
201  return value_ != 0;
202  }
203 
204  int get_numeric_value() const
205  {
206  return value_;
207  }
208 
209  virtual bool equals(variant_value_base& other) const override
210  {
211  return value_ == value_ref_cast<variant_numeric>(other).value_;
212  }
213 
214  virtual bool less_than(variant_value_base& other) const override
215  {
216  return value_ < value_ref_cast<variant_numeric>(other).value_;
217  }
218 
219 protected:
220  int value_;
221 };
222 
223 
225 {
226 public:
227  explicit variant_int(int value) : variant_numeric(value) {}
228 
229  variant build_range_variant(int limit) const;
230 
231  virtual std::string string_cast() const override
232  {
233  return std::to_string(value_);
234  }
235 
236  virtual std::string get_serialized_string() const override
237  {
238  return string_cast();
239  }
240 
241  virtual std::string get_debug_string(formula_seen_stack& /*seen*/, bool /*verbose*/) const override
242  {
243  return string_cast();
244  }
245 
246  virtual formula_variant::type get_type() const override
247  {
248  return formula_variant::type::integer;
249  }
250 };
251 
252 
254 {
255 public:
256  explicit variant_decimal(int value) : variant_numeric(value) {}
257 
258  explicit variant_decimal(double value) : variant_numeric(0)
259  {
260  value *= 1000;
261  value_ = static_cast<int>(value);
262  value -= value_;
263 
264  if(value > 0.5) {
265  value_++;
266  } else if(value < -0.5) {
267  value_--;
268  }
269  }
270 
271  virtual std::string string_cast() const override
272  {
273  return to_string_impl(false);
274  }
275 
276  virtual std::string get_serialized_string() const override
277  {
278  return to_string_impl(false);
279  }
280 
281  virtual std::string get_debug_string(formula_seen_stack& /*seen*/, bool /*verbose*/) const override
282  {
283  return to_string_impl(true);
284  }
285 
286  virtual formula_variant::type get_type() const override
287  {
288  return formula_variant::type::decimal;
289  }
290 
291 private:
292  std::string to_string_impl(const bool sign_value) const;
293 };
294 
295 
297 {
298 public:
299  explicit variant_callable(const_formula_callable_ptr callable);
301 
302  virtual bool as_bool() const override
303  {
304  return callable_ != nullptr;
305  }
306 
307  virtual std::size_t num_elements() const override
308  {
309  return 1;
310  }
311 
313  {
314  return callable_;
315  }
316 
317  virtual std::string string_cast() const override
318  {
319  return "(object)";
320  }
321 
322  virtual std::string get_serialized_string() const override;
323 
324  virtual std::string get_debug_string(formula_seen_stack& seen, bool verbose) const override;
325 
326  virtual bool equals(variant_value_base& other) const override;
327  virtual bool less_than(variant_value_base& other) const override;
328 
329  virtual formula_variant::type get_type() const override
330  {
331  return formula_variant::type::object;
332  }
333 
334  virtual boost::iterator_range<variant_iterator> make_iterator() const override;
335  virtual variant deref_iterator(const utils::any& iter) const override;
336 
337  virtual void iterator_inc(utils::any& iter) const override;
338  virtual void iterator_dec(utils::any& iter) const override;
339  virtual bool iterator_equals(const utils::any& /*first*/, const utils::any& /*second*/) const override
340  {
341  return true; // TODO: implement
342  }
343 
344 private:
345  void notify_dead() override {callable_.reset();}
346 
347  mutable formula_input_vector inputs; // for iteration
349 };
350 
351 
353 {
354 public:
355  explicit variant_string(const std::string& str) : string_(str) {}
356  explicit variant_string(std::string&& str) : string_(std::move(str)) {}
357 
358  virtual bool is_empty() const override
359  {
360  return string_.empty();
361  }
362 
363  virtual bool as_bool() const override
364  {
365  return !is_empty();
366  }
367 
368  const std::string& get_string() const
369  {
370  return string_;
371  }
372 
373  virtual std::string string_cast() const override
374  {
375  return string_;
376  }
377 
378  virtual std::string get_serialized_string() const override;
379 
380  virtual std::string get_debug_string(formula_seen_stack& /*seen*/, bool /*verbose*/) const override
381  {
382  return string_;
383  }
384 
385  virtual bool equals(variant_value_base& other) const override
386  {
387  return string_ == value_ref_cast<variant_string>(other).string_;
388  }
389 
390  virtual bool less_than(variant_value_base& other) const override
391  {
392  return string_ < value_ref_cast<variant_string>(other).string_;
393  }
394 
395  virtual formula_variant::type get_type() const override
396  {
397  return formula_variant::type::string;
398  }
399 
400 private:
401  std::string string_;
402 };
403 
404 /**
405  * Generalized implementation handling container variants.
406  *
407  * This class shouldn't usually be used directly. Instead, it's better to
408  * create a new derived class specialized to a specific container type.
409  */
410 template<typename T>
412 {
413  // NOTE: add more conditions if this changes.
414  static_assert((std::is_same_v<variant_vector, T> || std::is_same_v<variant_map_raw, T>),
415  "variant_container only accepts vector or map specifications.");
416 
417 public:
418  explicit variant_container(const T& container)
419  : container_(container)
420  {
421  }
422 
423  explicit variant_container(T&& container)
424  : container_(std::move(container))
425  {
426  }
427 
428  virtual bool is_empty() const override
429  {
430  return container_.empty();
431  }
432 
433  virtual std::size_t num_elements() const override
434  {
435  return container_.size();
436  }
437 
438  virtual bool as_bool() const override
439  {
440  return !is_empty();
441  }
442 
444  {
445  return container_;
446  }
447 
448  const T& get_container() const
449  {
450  return container_;
451  }
452 
453  virtual std::string string_cast() const override;
454 
455  virtual std::string get_serialized_string() const override;
456 
457  virtual std::string get_debug_string(formula_seen_stack& seen, bool verbose) const override;
458 
459  bool contains(const variant& member) const
460  {
461  return utils::contains<T, variant>(container_, member);
462  }
463 
464  // We implement these here since the interface is the same for all
465  // specializations and leave the deref function to the derived classes.
466  virtual boost::iterator_range<variant_iterator> make_iterator() const override;
467 
468  virtual void iterator_inc(utils::any&) const override;
469  virtual void iterator_dec(utils::any&) const override;
470  virtual bool iterator_equals(const utils::any& first, const utils::any& second) const override;
471 
472 protected:
473  using mod_func_t = std::function<std::string(const variant&)>;
474 
475  virtual std::string to_string_detail(const typename T::value_type& value, mod_func_t mod_func) const = 0;
476 
477 private:
478  /**
479  * Implementation to handle string conversion for @ref string_cast, @ref get_serialized_string,
480  * and @ref get_debug_string.
481  *
482  * Derived classes should provide type-specific value handling by implementing @ref to_string_detail.
483  */
484  std::string to_string_impl(bool annotate, bool annotate_empty, mod_func_t mod_func) const;
485 
487 };
488 
489 
490 class variant_list : public variant_container<variant_vector>
491 {
492 public:
493  explicit variant_list(const variant_vector& vec);
494  explicit variant_list(variant_vector&& vec);
495 
496  /**
497  * Applies the provided function to the corresponding variants in this and another list.
498  */
499  variant list_op(value_base_ptr second, const std::function<variant(variant&, variant&)>& op_func);
500 
501  virtual bool equals(variant_value_base& other) const override;
502  virtual bool less_than(variant_value_base& other) const override;
503 
504  virtual formula_variant::type get_type() const override
505  {
506  return formula_variant::type::list;
507  }
508 
509  virtual variant deref_iterator(const utils::any&) const override;
510 
511 private:
512  virtual std::string to_string_detail(const variant_vector::value_type& container_val, mod_func_t mod_func) const override
513  {
514  return mod_func(container_val);
515  }
516 };
517 
518 
519 class variant_map : public variant_container<variant_map_raw>
520 {
521 public:
522  explicit variant_map(const variant_map_raw& map)
523  : variant_container(map)
524  {}
525 
526  explicit variant_map(variant_map_raw&& map)
527  : variant_container(std::move(map))
528  {}
529 
530  virtual bool equals(variant_value_base& other) const override;
531  virtual bool less_than(variant_value_base& other) const override;
532 
533  virtual formula_variant::type get_type() const override
534  {
535  return formula_variant::type::map;
536  }
537 
538  virtual variant deref_iterator(const utils::any&) const override;
539 
540 private:
541  virtual std::string to_string_detail(const variant_map_raw::value_type& container_val, mod_func_t mod_func) const override;
542 };
543 
544 } // namespace wfl
MacOS doesn't support std::any_cast when targing MacOS < 10.14 (currently we target 10....
void notify_dead() override
virtual bool equals(variant_value_base &other) const override
Called to determine if this variant is equal to another of the same type.
virtual std::string get_debug_string(formula_seen_stack &seen, bool verbose) const override
Returns debug info for the variant value.
variant_callable(const_formula_callable_ptr callable)
virtual bool as_bool() const override
Returns a bool expression of the variant value.
virtual bool iterator_equals(const utils::any &, const utils::any &) const override
Implements the equality functionality of variant_iterator for a value of this type.
virtual std::string get_serialized_string() const override
Returns the stored variant value in formula syntax.
virtual variant deref_iterator(const utils::any &iter) const override
Implements the dereference functionality of variant_iterator for a value of this type.
virtual formula_variant::type get_type() const override
Returns the id of the variant type.
virtual std::string string_cast() const override
Returns the stored variant value in plain string form.
virtual void iterator_inc(utils::any &iter) const override
Implements the increment functionality of variant_iterator for a value of this type.
virtual std::size_t num_elements() const override
Returns the number of elements in a type.
virtual boost::iterator_range< variant_iterator > make_iterator() const override
Creates an iterator pair that can be used for iteration.
formula_input_vector inputs
virtual void iterator_dec(utils::any &iter) const override
Implements the decrement functionality of variant_iterator for a value of this type.
const_formula_callable_ptr callable_
const_formula_callable_ptr get_callable() const
virtual bool less_than(variant_value_base &other) const override
Called to determine if this variant is less than another of the same type.
Generalized implementation handling container variants.
virtual std::string get_serialized_string() const override
Returns the stored variant value in formula syntax.
std::string to_string_impl(bool annotate, bool annotate_empty, mod_func_t mod_func) const
Implementation to handle string conversion for string_cast, get_serialized_string,...
virtual std::string string_cast() const override
Returns the stored variant value in plain string form.
virtual void iterator_dec(utils::any &) const override
Implements the decrement functionality of variant_iterator for a value of this type.
virtual std::size_t num_elements() const override
Returns the number of elements in a type.
virtual std::string get_debug_string(formula_seen_stack &seen, bool verbose) const override
Returns debug info for the variant value.
variant_container(T &&container)
virtual boost::iterator_range< variant_iterator > make_iterator() const override
Creates an iterator pair that can be used for iteration.
const T & get_container() const
virtual bool is_empty() const override
Whether the stored value is considered empty or not.
std::function< std::string(const variant &)> mod_func_t
virtual bool as_bool() const override
Returns a bool expression of the variant value.
virtual bool iterator_equals(const utils::any &first, const utils::any &second) const override
Implements the equality functionality of variant_iterator for a value of this type.
virtual std::string to_string_detail(const typename T::value_type &value, mod_func_t mod_func) const =0
virtual void iterator_inc(utils::any &) const override
Implements the increment functionality of variant_iterator for a value of this type.
bool contains(const variant &member) const
variant_container(const T &container)
virtual formula_variant::type get_type() const override
Returns the id of the variant type.
std::string to_string_impl(const bool sign_value) const
variant_decimal(double value)
virtual std::string get_debug_string(formula_seen_stack &, bool) const override
Returns debug info for the variant value.
virtual std::string string_cast() const override
Returns the stored variant value in plain string form.
virtual std::string get_serialized_string() const override
Returns the stored variant value in formula syntax.
variant_int(int value)
variant build_range_variant(int limit) const
virtual std::string get_debug_string(formula_seen_stack &, bool) const override
Returns debug info for the variant value.
virtual std::string get_serialized_string() const override
Returns the stored variant value in formula syntax.
virtual formula_variant::type get_type() const override
Returns the id of the variant type.
virtual std::string string_cast() const override
Returns the stored variant value in plain string form.
variant_list(const variant_vector &vec)
virtual variant deref_iterator(const utils::any &) const override
Implements the dereference functionality of variant_iterator for a value of this type.
virtual std::string to_string_detail(const variant_vector::value_type &container_val, mod_func_t mod_func) const override
variant list_op(value_base_ptr second, const std::function< variant(variant &, variant &)> &op_func)
Applies the provided function to the corresponding variants in this and another list.
virtual bool less_than(variant_value_base &other) const override
Called to determine if this variant is less than another of the same type.
virtual bool equals(variant_value_base &other) const override
Called to determine if this variant is equal to another of the same type.
virtual formula_variant::type get_type() const override
Returns the id of the variant type.
variant_map(const variant_map_raw &map)
virtual std::string to_string_detail(const variant_map_raw::value_type &container_val, mod_func_t mod_func) const override
virtual variant deref_iterator(const utils::any &) const override
Implements the dereference functionality of variant_iterator for a value of this type.
virtual formula_variant::type get_type() const override
Returns the id of the variant type.
virtual bool less_than(variant_value_base &other) const override
Called to determine if this variant is less than another of the same type.
virtual bool equals(variant_value_base &other) const override
Called to determine if this variant is equal to another of the same type.
variant_map(variant_map_raw &&map)
Base class for numeric variant values.
int get_numeric_value() const
virtual bool less_than(variant_value_base &other) const override
Called to determine if this variant is less than another of the same type.
virtual bool equals(variant_value_base &other) const override
Called to determine if this variant is equal to another of the same type.
virtual bool as_bool() const override
Returns a bool expression of the variant value.
virtual std::string get_serialized_string() const override
Returns the stored variant value in formula syntax.
virtual bool as_bool() const override
Returns a bool expression of the variant value.
virtual std::string string_cast() const override
Returns the stored variant value in plain string form.
variant_string(const std::string &str)
virtual formula_variant::type get_type() const override
Returns the id of the variant type.
virtual std::string get_debug_string(formula_seen_stack &, bool) const override
Returns debug info for the variant value.
variant_string(std::string &&str)
virtual bool is_empty() const override
Whether the stored value is considered empty or not.
virtual bool equals(variant_value_base &other) const override
Called to determine if this variant is equal to another of the same type.
const std::string & get_string() const
virtual bool less_than(variant_value_base &other) const override
Called to determine if this variant is less than another of the same type.
Base class for all variant types.
virtual bool less_than(variant_value_base &) const
Called to determine if this variant is less than another of the same type.
virtual std::string get_debug_string(formula_seen_stack &, bool) const
Returns debug info for the variant value.
virtual bool iterator_equals(const utils::any &, const utils::any &) const
Implements the equality functionality of variant_iterator for a value of this type.
virtual boost::iterator_range< variant_iterator > make_iterator() const
Creates an iterator pair that can be used for iteration.
virtual bool as_bool() const
Returns a bool expression of the variant value.
virtual formula_variant::type get_type() const
Returns the id of the variant type.
virtual variant deref_iterator(const utils::any &iter) const
Implements the dereference functionality of variant_iterator for a value of this type.
virtual std::string string_cast() const
Returns the stored variant value in plain string form.
virtual void iterator_dec(utils::any &) const
Implements the decrement functionality of variant_iterator for a value of this type.
virtual bool equals(variant_value_base &) const
Called to determine if this variant is equal to another of the same type.
virtual void iterator_inc(utils::any &) const
Implements the increment functionality of variant_iterator for a value of this type.
virtual std::size_t num_elements() const
Returns the number of elements in a type.
virtual std::string get_serialized_string() const
Returns the stored variant value in formula syntax.
virtual bool is_empty() const
Whether the stored value is considered empty or not.
Definition: callable.hpp:26
static std::shared_ptr< T > value_cast(value_base_ptr ptr)
Casts a variant_value_base shared pointer to a new derived type.
std::shared_ptr< variant_value_base > value_base_ptr
std::vector< variant > variant_vector
std::vector< const_formula_callable_ptr > formula_seen_stack
std::vector< formula_input > formula_input_vector
std::map< variant, variant > variant_map_raw
std::shared_ptr< const formula_callable > const_formula_callable_ptr
static T & value_ref_cast(variant_value_base &ptr)
Casts a variant_value_base reference to a new derived type.
Base class for all the errors encountered by the engine.
Definition: exceptions.hpp:29
type_error(const std::string &str)
Definition: variant.cpp:55