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