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>
57 static const T& value_ref_cast(const variant_value_base& ptr)
58 {
59  try {
60  return dynamic_cast<const 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(const 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(const 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(const variant_value_base& other) const override
210  {
211  return value_ == value_ref_cast<variant_numeric>(other).value_;
212  }
213 
214  virtual bool less_than(const 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(const variant_value_base& other) const override;
327  virtual bool less_than(const 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(const variant_value_base& other) const override
386  {
387  return string_ == value_ref_cast<variant_string>(other).string_;
388  }
389 
390  virtual bool less_than(const 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 interface for container variants.
406  */
407 template<typename Derived>
409 {
410 protected:
411  /** Only derived classes can instantiate this class. */
412  variant_container() = default;
413 
414 public:
415  virtual bool is_empty() const override
416  {
417  return container().empty();
418  }
419 
420  virtual std::size_t num_elements() const override
421  {
422  return container().size();
423  }
424 
425  virtual bool as_bool() const override
426  {
427  return !is_empty();
428  }
429 
430  virtual std::string string_cast() const override;
431 
432  virtual std::string get_serialized_string() const override;
433 
434  virtual std::string get_debug_string(formula_seen_stack& seen, bool verbose) const override;
435 
436  bool contains(const variant& member) const
437  {
438  return utils::contains(container(), member);
439  }
440 
441  // We implement these here since the interface is the same for all
442  // specializations and leave the deref function to the derived classes.
443  virtual boost::iterator_range<variant_iterator> make_iterator() const override;
444 
445  virtual void iterator_inc(utils::any&) const override;
446  virtual void iterator_dec(utils::any&) const override;
447  virtual bool iterator_equals(const utils::any& first, const utils::any& second) const override;
448 
449  /** Inherited from variant_value_base. */
450  virtual bool equals(const variant_value_base& other) const override
451  {
452  return container_for(*this) == container_for(other);
453  }
454 
455  /** Inherited from variant_value_base. */
456  virtual bool less_than(const variant_value_base& other) const override
457  {
458  return container_for(*this) < container_for(other);
459  }
460 
461 protected:
462  using to_string_op = std::function<std::string(const variant&)>;
463 
464 private:
465  /**
466  * String conversion helper for @ref string_cast, @ref get_serialized_string,
467  * and @ref get_debug_string.
468  *
469  * Derived classes should implement container-specific handling by defining a
470  * static to_string_detail function which takes the container's value_type as
471  * its first parameter and a to_string_op functor as its second.
472  */
473  std::string to_string_impl(bool annotate, bool annotate_empty, const to_string_op& mod_func) const;
474 
475  /** Read-only access to the underlying container. */
476  const auto& container() const
477  {
478  return container_for(*this);
479  }
480 
481  /** Helper to call get_container for the derived class. */
482  static const auto& container_for(const variant_value_base& value)
483  {
484  return static_cast<const Derived&>(value).get_container();
485  }
486 };
487 
488 
489 class variant_list : public variant_container<variant_list>
490 {
491 public:
492  friend class variant_container<variant_list>;
493 
494  explicit variant_list(const variant_vector& vec)
495  : container_(vec)
496  {
497  }
498 
499  explicit variant_list(variant_vector&& vec)
500  : container_(std::move(vec))
501  {
502  }
503 
505  {
506  return container_;
507  }
508 
509 
510  virtual formula_variant::type get_type() const override
511  {
512  return formula_variant::type::list;
513  }
514 
515  virtual variant deref_iterator(const utils::any&) const override;
516 
517 private:
518  /** Helper for @ref variant_container::to_string_impl. */
519  static std::string to_string_detail(const variant& value, const to_string_op& op)
520  {
521  return op(value);
522  }
523 
525 };
526 
527 
528 class variant_map : public variant_container<variant_map>
529 {
530 public:
531  friend class variant_container<variant_map>;
532 
533  explicit variant_map(const variant_map_raw& map)
534  : container_(map)
535  {
536  }
537 
538  explicit variant_map(variant_map_raw&& map)
539  : container_(std::move(map))
540  {
541  }
542 
544  {
545  return container_;
546  }
547 
548 
549  virtual formula_variant::type get_type() const override
550  {
551  return formula_variant::type::map;
552  }
553 
554  virtual variant deref_iterator(const utils::any&) const override;
555 
556 private:
557  /** Helper for @ref variant_container::to_string_impl. */
558  static std::string to_string_detail(const variant_map_raw::value_type& value, const to_string_op& op);
559 
561 };
562 
563 } // 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(const 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(const variant_value_base &other) const override
Called to determine if this variant is less than another of the same type.
Generalized interface for container variants.
virtual std::string get_serialized_string() const override
Returns the stored variant value in formula syntax.
virtual bool equals(const variant_value_base &other) const override
Inherited from variant_value_base.
virtual std::string string_cast() const override
Returns the stored variant value in plain string form.
virtual std::size_t num_elements() const override
Returns the number of elements in a type.
virtual void iterator_dec(utils::any &) const override
Implements the decrement functionality of variant_iterator for a value of this type.
virtual bool less_than(const variant_value_base &other) const override
Inherited from variant_value_base.
variant_container()=default
Only derived classes can instantiate this class.
virtual std::string get_debug_string(formula_seen_stack &seen, bool verbose) const override
Returns debug info for the variant value.
bool contains(const variant &member) const
const auto & container() const
Read-only access to the underlying container.
virtual boost::iterator_range< variant_iterator > make_iterator() const override
Creates an iterator pair that can be used for iteration.
std::function< std::string(const variant &)> to_string_op
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 void iterator_inc(utils::any &) const override
Implements the increment functionality of variant_iterator for a value of this type.
std::string to_string_impl(bool annotate, bool annotate_empty, const to_string_op &mod_func) const
String conversion helper for string_cast, get_serialized_string, and get_debug_string.
static const auto & container_for(const variant_value_base &value)
Helper to call get_container for the derived class.
virtual bool as_bool() const override
Returns a bool expression of the variant value.
virtual bool is_empty() const override
Whether the stored value is considered empty or not.
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.
static std::string to_string_detail(const variant &value, const to_string_op &op)
Helper for variant_container::to_string_impl.
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(variant_vector &&vec)
const variant_vector & get_container() const
variant_vector container_
virtual formula_variant::type get_type() const override
Returns the id of the variant type.
variant_map(const variant_map_raw &map)
virtual variant deref_iterator(const utils::any &) const override
Implements the dereference functionality of variant_iterator for a value of this type.
const variant_map_raw & get_container() const
virtual formula_variant::type get_type() const override
Returns the id of the variant type.
static std::string to_string_detail(const variant_map_raw::value_type &value, const to_string_op &op)
Helper for variant_container::to_string_impl.
variant_map_raw container_
variant_map(variant_map_raw &&map)
Base class for numeric variant values.
int get_numeric_value() const
virtual bool less_than(const variant_value_base &other) const override
Called to determine if this variant is less than another of the same type.
virtual bool equals(const 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.
virtual bool equals(const variant_value_base &other) const override
Called to determine if this variant is equal to another of the same type.
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 less_than(const variant_value_base &other) const override
Called to determine if this variant is less than another of the same type.
const std::string & get_string() const
Base class for all variant types.
virtual bool equals(const variant_value_base &) const
Called to determine if this variant is equal to 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 bool less_than(const variant_value_base &) const
Called to determine if this variant is less than another of the same 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 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.
bool contains(const Container &container, const Value &value)
Returns true iff value is found in container.
Definition: general.hpp:87
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 const T & value_ref_cast(const 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