The Battle for Wesnoth  1.19.5+dev
variant_value.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2017 - 2024
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 */
133  virtual const formula_variant::type& get_type() const
134  {
135  static formula_variant::type type = formula_variant::type::null;
136  return type;
137  }
138 
139  /**
140  * Creates an iterator pair that can be used for iteration.
141  * For an iterable type, it should use the two-argument constructor of variant-iterator,
142  * passing the underlying iterator as the utils::any parameter.
143  *
144  * This creates both the begin and end iterator, but the variant implementation
145  * discards one of the two.
146  */
147  virtual boost::iterator_range<variant_iterator> make_iterator() const;
148 
149  /**
150  * Implements the dereference functionality of variant_iterator
151  * for a value of this type.
152  *
153  * @param iter The opaque reference that was passed to the variant_iterator by @ref make_iterator.
154  */
155  virtual variant deref_iterator(const utils::any& iter) const;
156 
157  /**
158  * Implements the increment functionality of variant_iterator
159  * for a value of this type.
160  *
161  * The parameter is an opaque reference that was passed to the variant_iterator by @ref make_iterator.
162  */
163  virtual void iterator_inc(utils::any&) const {}
164 
165  /**
166  * Implements the decrement functionality of variant_iterator
167  * for a value of this type.
168  *
169  * The parameter is an opaque reference that was passed to the variant_iterator by @ref make_iterator.
170  */
171  virtual void iterator_dec(utils::any&) const {}
172 
173  /**
174  * Implements the equality functionality of variant_iterator
175  * for a value of this type.
176  *
177  * Note that this is only called if the two iterators are already known to be of the same type.
178  *
179  * The first parameter is an opaque reference that was passed to the variant_iterator by @ref make_iterator.
180  * The second parameter is an opaque reference that was passed to the variant_iterator by @ref make_iterator.
181  */
182  virtual bool iterator_equals(const utils::any& /*first*/, const utils::any& /*second*/) const
183  {
184  return true;
185  }
186 
187  virtual ~variant_value_base() {}
188 };
189 
190 
191 /**
192  * Base class for numeric variant values. Currently only supports a value stored as an
193  * integer, but for now, that's all that's necessary.
194  */
196 {
197 public:
198  explicit variant_numeric(int value) : value_(value) {}
199 
200  virtual bool as_bool() const override
201  {
202  return value_ != 0;
203  }
204 
205  int get_numeric_value() const
206  {
207  return value_;
208  }
209 
210  virtual bool equals(variant_value_base& other) const override
211  {
212  return value_ == value_ref_cast<variant_numeric>(other).value_;
213  }
214 
215  virtual bool less_than(variant_value_base& other) const override
216  {
217  return value_ < value_ref_cast<variant_numeric>(other).value_;
218  }
219 
220 protected:
221  int value_;
222 };
223 
224 
226 {
227 public:
228  explicit variant_int(int value) : variant_numeric(value) {}
229 
230  variant build_range_variant(int limit) const;
231 
232  virtual std::string string_cast() const override
233  {
234  return std::to_string(value_);
235  }
236 
237  virtual std::string get_serialized_string() const override
238  {
239  return string_cast();
240  }
241 
242  virtual std::string get_debug_string(formula_seen_stack& /*seen*/, bool /*verbose*/) const override
243  {
244  return string_cast();
245  }
246 
247  virtual const formula_variant::type& get_type() const override
248  {
249  static formula_variant::type type = formula_variant::type::integer;
250  return type;
251  }
252 };
253 
254 
256 {
257 public:
258  explicit variant_decimal(int value) : variant_numeric(value) {}
259 
260  explicit variant_decimal(double value) : variant_numeric(0)
261  {
262  value *= 1000;
263  value_ = static_cast<int>(value);
264  value -= value_;
265 
266  if(value > 0.5) {
267  value_++;
268  } else if(value < -0.5) {
269  value_--;
270  }
271  }
272 
273  virtual std::string string_cast() const override
274  {
275  return to_string_impl(false);
276  }
277 
278  virtual std::string get_serialized_string() const override
279  {
280  return to_string_impl(false);
281  }
282 
283  virtual std::string get_debug_string(formula_seen_stack& /*seen*/, bool /*verbose*/) const override
284  {
285  return to_string_impl(true);
286  }
287 
288  virtual const formula_variant::type& get_type() const override
289  {
290  static formula_variant::type type = formula_variant::type::decimal;
291  return type;
292  }
293 
294 private:
295  std::string to_string_impl(const bool sign_value) const;
296 };
297 
298 
300 {
301 public:
302  explicit variant_callable(const_formula_callable_ptr callable);
304 
305  virtual bool as_bool() const override
306  {
307  return callable_ != nullptr;
308  }
309 
310  virtual std::size_t num_elements() const override
311  {
312  return 1;
313  }
314 
316  {
317  return callable_;
318  }
319 
320  virtual std::string string_cast() const override
321  {
322  return "(object)";
323  }
324 
325  virtual std::string get_serialized_string() const override;
326 
327  virtual std::string get_debug_string(formula_seen_stack& seen, bool verbose) const override;
328 
329  virtual bool equals(variant_value_base& other) const override;
330  virtual bool less_than(variant_value_base& other) const override;
331 
332  virtual const formula_variant::type& get_type() const override
333  {
334  static formula_variant::type type = formula_variant::type::object;
335  return type;
336  }
337 
338  virtual boost::iterator_range<variant_iterator> make_iterator() const override;
339  virtual variant deref_iterator(const utils::any& iter) const override;
340 
341  virtual void iterator_inc(utils::any& iter) const override;
342  virtual void iterator_dec(utils::any& iter) const override;
343  virtual bool iterator_equals(const utils::any& /*first*/, const utils::any& /*second*/) const override
344  {
345  return true; // TODO: implement
346  }
347 
348 private:
349  void notify_dead() override {callable_.reset();}
350 
351  mutable formula_input_vector inputs; // for iteration
353 };
354 
355 
357 {
358 public:
359  explicit variant_string(const std::string& str) : string_(str) {}
360 
361  virtual bool is_empty() const override
362  {
363  return string_.empty();
364  }
365 
366  virtual bool as_bool() const override
367  {
368  return !is_empty();
369  }
370 
371  const std::string& get_string() const
372  {
373  return string_;
374  }
375 
376  virtual std::string string_cast() const override
377  {
378  return string_;
379  }
380 
381  virtual std::string get_serialized_string() const override;
382 
383  virtual std::string get_debug_string(formula_seen_stack& /*seen*/, bool /*verbose*/) const override
384  {
385  return string_;
386  }
387 
388  virtual bool equals(variant_value_base& other) const override
389  {
390  return string_ == value_ref_cast<variant_string>(other).string_;
391  }
392 
393  virtual bool less_than(variant_value_base& other) const override
394  {
395  return string_ < value_ref_cast<variant_string>(other).string_;
396  }
397 
398  virtual const formula_variant::type& get_type() const override
399  {
400  static formula_variant::type type = formula_variant::type::string;
401  return type;
402  }
403 
404 private:
405  std::string string_;
406 };
407 
408 /**
409  * Generalized implementation handling container variants.
410  *
411  * This class shouldn't usually be used directly. Instead, it's better to
412  * create a new derived class specialized to a specific container type.
413  */
414 template<typename T>
416 {
417 public:
418  explicit variant_container(const T& container)
419  : container_(container)
420  {
421  // NOTE: add more conditions if this changes.
422  static_assert((std::is_same_v<variant_vector, T> || std::is_same_v<variant_map_raw, T>),
423  "variant_container only accepts vector or map specifications.");
424  }
425 
426  virtual bool is_empty() const override
427  {
428  return container_.empty();
429  }
430 
431  virtual std::size_t num_elements() const override
432  {
433  return container_.size();
434  }
435 
436  virtual bool as_bool() const override
437  {
438  return !is_empty();
439  }
440 
442  {
443  return container_;
444  }
445 
446  const T& get_container() const
447  {
448  return container_;
449  }
450 
451  virtual std::string string_cast() const override;
452 
453  virtual std::string get_serialized_string() const override;
454 
455  virtual std::string get_debug_string(formula_seen_stack& seen, bool verbose) const override;
456 
457  bool contains(const variant& member) const
458  {
459  return utils::contains<T, variant>(container_, member);
460  }
461 
462  // We implement these here since the interface is the same for all
463  // specializations and leave the deref function to the derived classes.
464  virtual boost::iterator_range<variant_iterator> make_iterator() const override;
465 
466  virtual void iterator_inc(utils::any&) const override;
467  virtual void iterator_dec(utils::any&) const override;
468  virtual bool iterator_equals(const utils::any& first, const utils::any& second) const override;
469 
470 protected:
471  using mod_func_t = std::function<std::string(const variant&)>;
472 
473  virtual std::string to_string_detail(const typename T::value_type& value, mod_func_t mod_func) const = 0;
474 
475 private:
476  /**
477  * Implementation to handle string conversion for @ref string_cast, @ref get_serialized_string,
478  * and @ref get_debug_string.
479  *
480  * Derived classes should provide type-specific value handling by implementing @ref to_string_detail.
481  */
482  std::string to_string_impl(bool annotate, bool annotate_empty, mod_func_t mod_func) const;
483 
485 };
486 
487 
488 class variant_list : public variant_container<variant_vector>
489 {
490 public:
491  explicit variant_list(const variant_vector& vec);
492 
493  /**
494  * Applies the provided function to the corresponding variants in this and another list.
495  */
496  variant list_op(value_base_ptr second, std::function<variant(variant&, variant&)> op_func);
497 
498  virtual bool equals(variant_value_base& other) const override;
499  virtual bool less_than(variant_value_base& other) const override;
500 
501  virtual const formula_variant::type& get_type() const override
502  {
503  static formula_variant::type type = formula_variant::type::list;
504  return type;
505  }
506 
507  virtual variant deref_iterator(const utils::any&) const override;
508 
509 private:
510  virtual std::string to_string_detail(const variant_vector::value_type& container_val, mod_func_t mod_func) const override
511  {
512  return mod_func(container_val);
513  }
514 };
515 
516 
517 class variant_map : public variant_container<variant_map_raw>
518 {
519 public:
520  explicit variant_map(const variant_map_raw& map)
522  {}
523 
524  virtual bool equals(variant_value_base& other) const override;
525  virtual bool less_than(variant_value_base& other) const override;
526 
527  virtual const formula_variant::type& get_type() const override
528  {
529  static formula_variant::type type = formula_variant::type::map;
530  return type;
531  }
532 
533  virtual variant deref_iterator(const utils::any&) const override;
534 
535 private:
536  virtual std::string to_string_detail(const variant_map_raw::value_type& container_val, mod_func_t mod_func) const override;
537 };
538 
539 } // 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 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.
virtual const formula_variant::type & get_type() const override
Returns the id of the variant 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.
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)
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.
virtual const formula_variant::type & get_type() const override
Returns the id of the variant type.
variant_int(int value)
variant build_range_variant(int limit) const
virtual const 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.
virtual std::string get_serialized_string() const override
Returns the stored variant value in formula syntax.
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.
variant list_op(value_base_ptr second, std::function< variant(variant &, variant &)> op_func)
Applies the provided function to the corresponding variants in this and another list.
virtual const formula_variant::type & get_type() const override
Returns the id of the variant type.
virtual std::string to_string_detail(const variant_vector::value_type &container_val, mod_func_t mod_func) const override
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(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 bool less_than(variant_value_base &other) const override
Called to determine if this variant is less than another of the same type.
virtual const formula_variant::type & get_type() const override
Returns the id of the variant type.
virtual bool equals(variant_value_base &other) const override
Called to determine if this variant is equal to another of the same type.
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 const 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_string(const std::string &str)
virtual std::string get_debug_string(formula_seen_stack &, bool) const override
Returns debug info for the variant value.
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 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 const formula_variant::type & get_type() const
Returns the id of the variant 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: contexts.hpp:43
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