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