FireBreath  1.4.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Pages
variant.h
1 /**********************************************************\
2 Created: 2008-2012
3 License: Dual license model; choose one of two:
4  New BSD License
5  http://www.opensource.org/licenses/bsd-license.php
6  - or -
7  GNU Lesser General Public License, version 2.1
8  http://www.gnu.org/licenses/lgpl-2.1.html
9 
10 Copyright 2008-2012 Richard Bateman, Firebreath development team
11 \**********************************************************/
12 
13 #pragma once
14 #ifndef FB_VARIANT_H
15 #define FB_VARIANT_H
16 
17 //#define ANY_IMPLICIT_CASTING // added to enable implicit casting
18 
19 #include <boost/cstdint.hpp>
20 #include <boost/any.hpp>
21 #include <stdexcept>
22 #include <typeinfo>
23 #include <algorithm>
24 #include <cstdlib>
25 #include <sstream>
26 #include <string>
27 
28 #include <boost/type_traits/is_integral.hpp>
29 #include <boost/type_traits/is_floating_point.hpp>
30 #include <boost/type_traits/is_arithmetic.hpp>
31 #include <boost/mpl/or.hpp>
32 #include <boost/mpl/and.hpp>
33 #include <boost/mpl/not.hpp>
34 #include <boost/mpl/for_each.hpp>
35 #include <boost/numeric/conversion/cast.hpp>
36 #include <boost/lexical_cast.hpp>
37 #include <boost/optional.hpp>
38 #include <boost/variant/variant_fwd.hpp>
39 #include <boost/variant/apply_visitor.hpp>
40 #include <boost/variant/static_visitor.hpp>
41 #include <boost/tuple/tuple.hpp>
42 #include <boost/preprocessor/tuple/to_seq.hpp>
43 #include <boost/logic/tribool.hpp>
44 
45 #include "APITypes.h"
46 #include "Util/meta_util.h"
47 #include "utf8_tools.h"
48 #include "variant_conversions.h"
49 
50 #ifdef _WIN32
51 #pragma warning(push)
52 #pragma warning( disable : 4800 )
53 #endif
54 
55 #define FB_BEGIN_CONVERT_MAP(_type_) \
56  const std::type_info *type(&var.get_type()); \
57  if (*type == typeid(_type_)) { \
58  return var.cast< _type_ >(); \
59  } else
60 
61 #define FB_END_CONVERT_MAP(_type_) { throw bad_variant_cast(var.get_type(), typeid(_type_)); }
62 #define FB_END_CONVERT_MAP_NO_THROW(_type_) {}
63 
64 #define FB_CONVERT_ENTRY_SIMPLE(_type_, _srctype_) \
65  if ( *type == typeid( _srctype_ ) ) { \
66  return static_cast< _type_ >( var.cast< _srctype_ >() );\
67  } else
68 
69 #define FB_CONVERT_ENTRY_COMPLEX_BEGIN(_srctype_, _var_) \
70  if (*type == typeid(_srctype_)) { \
71  _srctype_ _var_ = var.cast<_srctype_>();
72 
73 #define FB_CONVERT_ENTRY_COMPLEX_END() \
74  } else
75 
76 #define FB_CONVERT_ENTRY_NUMERIC(_type_, _srctype_) \
77  if (*type == typeid(_srctype_)) { \
78  try { \
79  return boost::numeric_cast<_type_>(var.cast<_srctype_>());\
80  } catch (const boost::numeric::bad_numeric_cast& ) { \
81  throw bad_variant_cast(var.get_type(), typeid(_type_)); \
82  } \
83  } else
84 
85 #define FB_CONVERT_ENTRY_FROM_WSTRING(_type_, _srctype_) \
86  if (*type == typeid(_srctype_)) { \
87  std::string __tmp = FB::wstring_to_utf8(var.cast<_srctype_>()); \
88  std::istringstream iss(__tmp); \
89  _type_ to; \
90  if (iss >> to) { \
91  return to; \
92  } else { \
93  throw bad_variant_cast(var.get_type(), typeid(_type_)); \
94  } \
95  } else
96 
97 #define FB_CONVERT_ENTRY_FROM_STRING(_type_, _srctype_) \
98  if (*type == typeid(_srctype_)) { \
99  typedef _srctype_::value_type char_type; \
100  std::basic_istringstream<char_type> iss(var.cast<_srctype_>()); \
101  _type_ to; \
102  if (iss >> to) { \
103  return to; \
104  } else { \
105  throw bad_variant_cast(var.get_type(), typeid(_type_)); \
106  } \
107  } else
108 
109 #define FB_CONVERT_ENTRY_FROM_STRING_TYPE(_type_, _srctype_) \
110  if (*type == typeid(_srctype_)) { \
111  typedef _type_::value_type char_type; \
112  std::basic_ostringstream<char_type> oss; \
113  if (oss << var.cast<_srctype_>()) { \
114  return oss.str(); \
115  } else { \
116  throw bad_variant_cast(var.get_type(), typeid(_type_)); \
117  } \
118  } else
119 
120 #define FB_CONVERT_ENTRY_TO_STRING(_srctype_) FB_CONVERT_ENTRY_FROM_STRING_TYPE(std::string , _srctype_)
121 #define FB_CONVERT_ENTRY_TO_WSTRING(_srctype_) FB_CONVERT_ENTRY_FROM_STRING_TYPE(std::wstring, _srctype_)
122 
123 namespace FB
124 {
125  class JSObject;
126 
133  struct bad_variant_cast : std::bad_cast {
134  bad_variant_cast(const std::type_info& src, const std::type_info& dest)
135  : from(src.name()), to(dest.name())
136  { }
137  virtual const char* what() const throw() {
138  return "bad cast";
139  }
140  const char* from;
141  const char* to;
142  };
143 
144  namespace variant_detail {
145  struct null {
146  bool operator<(const null&) const {
147  return false;
148  }
149  };
150 
151  struct empty {
152  bool operator<(const empty&) const {
153  return false;
154  }
155  };
156 
157  template<typename T>
158  struct lessthan {
159  static bool impl(const boost::any& l, const boost::any& r) {
160  return boost::any_cast<T>(l) < boost::any_cast<T>(r);
161  }
162  };
163  } // namespace variant_detail
164 
165  class variant;
166 
167  template <class T>
168  variant make_variant(T);
169 
198  class variant
199  {
200  public:
208  template <typename T>
209  variant(const T& x, bool) {
210  assign(x, true);
211  }
212 
213  template <typename T>
214  variant(const T& x) {
215  assign(x);
216  }
217 
224  : lessthan(&FB::variant::lessthan_default) {
225  }
226 
236  variant& assign(const variant& x) {
237  object = x.object;
238  lessthan = x.lessthan;
239  return *this;
240  }
241 
242  template<class T>
243  variant& assign(const T& x) {
244  return assign(make_variant(x));
245  // If you get an error that there are no overloads that could convert all the argument
246  // types for this line, you are trying to use a type that is not known to FireBreath.
247  // First, make sure you really want to use this type! If you aren't doing this on
248  // purpose, double check your type, because it's wrong!
249  //
250  // Alternately, if you mean to do this there are two options:
251  // 1. You can create a variant or call .assign on a variant with a second bool
252  // parameter, like so:
253  // FB::variant tmp((void*)0x12, true); // Forces creation with unknown type
254  // tmp.assign((int*)0x32, true); // Forces assignment of unknown type
255  //
256  // 2. You could either create your own assignment function by either overriding
257  // FB::make_variant or FB::variant_detail::conversion::make_variant.
258  //
259  }
260 
270  template <typename T>
271  variant& assign(const T& x, bool) {
272  object = x;
273  lessthan = &FB::variant_detail::lessthan<T>::impl;
274  return *this;
275  }
276 
277  // assignment operator
278  template<typename T>
279  variant& operator=(T const& x) {
280  return assign(x);
281  }
282 
283  // utility functions
284  variant& swap(variant& x) {
285  std::swap(object, x.object);
286  std::swap(lessthan, x.lessthan);
287  return *this;
288  }
289 
290  // comparison function
291  bool operator<(const variant& rh) const {
292  if (get_type() == rh.get_type()) {
293  return lessthan(object, rh.object);
294  }
295  const char* left = get_type().name();
296  const char* right = rh.get_type().name();
297  return strcmp(left, right) < 0;
298  }
299 
307  const std::type_info& get_type() const {
308  return object.type();
309  }
310 
326  template<typename T>
327  bool can_be_type() const {
328  if (get_type() == typeid(T))
329  return true;
330  try {
331  // See if it can be that type by converting
332  convert_cast<T>();
333  return true;
334  } catch (...) {
335  return false;
336  }
337  }
338 
354  template<typename T>
355  bool is_of_type() const {
356  return (get_type() == typeid(T));
357  }
358 
369  template<typename T>
370  T cast() const {
371  if (get_type() != typeid(T)) {
372  throw bad_variant_cast(get_type(), typeid(T));
373  }
374  return boost::any_cast<T>(object);
375  }
376 
393  template<typename T>
394  const T convert_cast() const;
395 
403  bool empty() const {
404  return object.empty() || is_of_type<FB::FBVoid>();
405  }
406 
407  bool is_null() const {
408  return is_of_type<FB::FBNull>();
409  }
410 
416  void reset() {
417  assign(variant());
418  }
419 
420  private:
421  template<typename T>
422  const T convert_cast_impl() const {
423  return cast<T>();
424  }
425 
426  static bool lessthan_default(const boost::any& l, const boost::any& r) {
427  return false;
428  }
429 
430  // fields
431  boost::any object;
432  bool (*lessthan)(const boost::any&, const boost::any&);
433  };
434 
435  template <>
436  inline const std::string variant::convert_cast<std::string>() const {
437  variant var = *this;
438  FB_BEGIN_CONVERT_MAP(std::string);
439  FB_CONVERT_ENTRY_TO_STRING(double);
440  FB_CONVERT_ENTRY_TO_STRING(float);
441  FB_CONVERT_ENTRY_TO_STRING(int);
442  FB_CONVERT_ENTRY_TO_STRING(unsigned int);
443  FB_CONVERT_ENTRY_COMPLEX_BEGIN(bool, bval);
444  return bval ? "true" : "false";
445  FB_CONVERT_ENTRY_COMPLEX_END();
446  FB_CONVERT_ENTRY_COMPLEX_BEGIN(std::wstring, str);
447  return wstring_to_utf8(str);
448  FB_CONVERT_ENTRY_COMPLEX_END();
449  FB_CONVERT_ENTRY_TO_STRING(long);
450  FB_CONVERT_ENTRY_TO_STRING(unsigned long);
451  FB_CONVERT_ENTRY_TO_STRING(short);
452  FB_CONVERT_ENTRY_TO_STRING(unsigned short);
453  FB_CONVERT_ENTRY_TO_STRING(char);
454  FB_CONVERT_ENTRY_TO_STRING(unsigned char);
455  FB_CONVERT_ENTRY_TO_STRING(boost::int64_t);
456  FB_CONVERT_ENTRY_TO_STRING(boost::uint64_t);
457  FB_END_CONVERT_MAP(std::string);
458  }
459 
460  template<>
461  inline const std::wstring variant::convert_cast<std::wstring>() const {
462  variant var = *this;
463  FB_BEGIN_CONVERT_MAP(std::wstring);
464  FB_CONVERT_ENTRY_TO_WSTRING(double);
465  FB_CONVERT_ENTRY_TO_WSTRING(float);
466  FB_CONVERT_ENTRY_TO_WSTRING(int);
467  FB_CONVERT_ENTRY_TO_WSTRING(unsigned int);
468  FB_CONVERT_ENTRY_COMPLEX_BEGIN(bool, bval);
469  return bval ? L"true" : L"false";
470  FB_CONVERT_ENTRY_COMPLEX_END();
471  FB_CONVERT_ENTRY_COMPLEX_BEGIN(std::string, str);
472  return utf8_to_wstring(str);
473  FB_CONVERT_ENTRY_COMPLEX_END();
474  FB_CONVERT_ENTRY_TO_WSTRING(long);
475  FB_CONVERT_ENTRY_TO_WSTRING(unsigned long);
476  FB_CONVERT_ENTRY_TO_WSTRING(short);
477  FB_CONVERT_ENTRY_TO_WSTRING(unsigned short);
478  FB_CONVERT_ENTRY_TO_WSTRING(char);
479  FB_CONVERT_ENTRY_TO_WSTRING(unsigned char);
480  FB_END_CONVERT_MAP(std::wstring);
481  }
482 
483  template<>
484  inline const bool variant::convert_cast<bool>() const {
485  variant var = *this;
486  FB_BEGIN_CONVERT_MAP(bool);
487  FB_CONVERT_ENTRY_COMPLEX_BEGIN(std::string, str);
488  std::transform(str.begin(), str.end(), str.begin(), ::tolower);
489  return (str == "y" || str == "1" || str == "yes" || str == "true" || str == "t");
490  FB_CONVERT_ENTRY_COMPLEX_END();
491  FB_CONVERT_ENTRY_COMPLEX_BEGIN(std::wstring, str);
492  std::transform(str.begin(), str.end(), str.begin(), ::tolower);
493  return (str == L"y" || str == L"1" || str == L"yes" || str == L"true" || str == L"t");
494  FB_CONVERT_ENTRY_COMPLEX_END();
495  FB_END_CONVERT_MAP_NO_THROW(short);
496 
497  return convert_cast<long>();
498  }
499 
500  namespace variant_detail {
501  namespace conversion {
503  // variant assign conversion functions
504  //
505  // These functions are called to process any
506  // values assigned to the variant. For example,
507  // all const char* parameters are converted to
508  // std::string
510  template <class T>
511  typename boost::enable_if<
512  boost::mpl::or_<
513  boost::mpl::or_<
514  boost::mpl::or_<
515  boost::is_same<std::vector<variant>, T>,
516  boost::is_same<std::map<std::string, variant>, T>
517  >,
518  boost::mpl::or_<
519  boost::is_same<std::wstring, T>,
520  boost::is_same<std::string, T>
521  >
522  >,
523  boost::mpl::or_<
524  boost::is_same<variant_detail::empty, T>,
525  boost::is_same<variant_detail::null, T>
526  >
527  >, variant>::type
528  make_variant(const T& t) {
529  return variant(t, true);
530  }
531 
532  template <class T>
533  variant make_variant(const boost::optional<T>& val) {
534  if (val)
535  return variant(*val);
536  else
537  return variant();
538  }
539 
540  inline variant make_variant(const FB::JSAPIWeakPtr& ptr) {
541  return variant(ptr, true);
542  }
543 
544  variant make_variant(const boost::tribool& val);
545  boost::tribool convert_variant( const FB::variant& var, const type_spec<boost::tribool>& );
546 
547  template <class T>
548  typename boost::enable_if<
549  boost::mpl::and_<
550  boost::is_convertible<T, int>,
551  boost::mpl::not_<boost::is_arithmetic<T> >
552  >, variant>::type
553  make_variant(const T t) {
554  return variant(static_cast<int>(t), true);
555  }
556 
557  template <class T>
558  typename boost::enable_if<boost::is_arithmetic<T>, variant>::type
559  make_variant(const T t) {
560  return variant(t, true);
561  }
562 
563  variant make_variant(const char* x);
564  variant make_variant(const wchar_t* x);
566  // variant convert_cast helpers
567  //
568  // These functions are called to process any
569  // values assigned to the variant. For example,
570  // all const char* parameters are converted to
571  // std::string
573  struct boost_variant_to_FBVariant
574  : public boost::static_visitor<FB::variant> {
575  template <typename T>
576  FB::variant operator()(T inVal) const {
577  return FB::variant(inVal);
578  }
579  };
580 
581  template <class T>
582  typename boost::enable_if<
583  FB::meta::is_boost_variant<T>, variant
584  >::type
585  make_variant(T& inVal) {
586  return boost::apply_visitor(boost_variant_to_FBVariant(), inVal);
587  }
588 
589  template <typename V>
590  struct FBVariant_to_boost_variant {
591  V* res;
592  bool* found;
593  const variant& val;
594  FBVariant_to_boost_variant(const variant& val, bool* b, V* v) : res(v), found(b), val(val) {}
595 
596  template <typename T>
597  void operator()(type_spec<T>&) {
598  if (typeid(T) == val.get_type()) {
599  // If we find an exact type match, use it
600  *res = val.cast<T>();
601  *found = true;
602  return;
603  }
604  else if (*found) return; // When we find the correct cast we can be done
605  try {
606  // If we haven't found an exact match, go for the next best match
607  *res = val.convert_cast<T>();
608  *found = true;
609  } catch (const FB::bad_variant_cast&) {
610  }
611  }
612  };
613 
614  template <class T>
615  T convert_variant(const variant& var, const type_spec<T>,
616  typename boost::enable_if<FB::meta::is_boost_variant<T> >::type*p = 0) {
617  bool found(false);
618  T res;
619  FBVariant_to_boost_variant<T> converter(var, &found, &res);
620  boost::mpl::for_each<typename T::types, type_spec<boost::mpl::_1> >(converter);
621  if (found)
622  return res;
623  else
624  throw FB::bad_variant_cast(var.get_type(), typeid(T));
625  }
626 
627  const void convert_variant(const variant&, const type_spec<void>);
628  const FB::FBNull convert_variant(const variant&, const type_spec<FBNull>);
629  const FB::FBVoid convert_variant(const variant&, const type_spec<FBVoid>);
630  const variant& convert_variant(const variant& var, const type_spec<variant>);
631 
632  template<typename T>
633  boost::optional<T> convert_variant(const variant& var, const type_spec<boost::optional<T> >) {
634  if (var.is_null() || var.empty())
635  return boost::optional<T>();
636 
637  return var.convert_cast<T>();
638  }
639 
640  template<typename T>
641  typename FB::meta::enable_for_numbers<T, T>::type
642  convert_variant(const variant& var, const type_spec<T>&) {
643  FB_BEGIN_CONVERT_MAP(T)
644  FB_CONVERT_ENTRY_NUMERIC(T, char)
645  FB_CONVERT_ENTRY_NUMERIC(T, unsigned char)
646  FB_CONVERT_ENTRY_NUMERIC(T, short)
647  FB_CONVERT_ENTRY_NUMERIC(T, unsigned short)
648  FB_CONVERT_ENTRY_NUMERIC(T, int)
649  FB_CONVERT_ENTRY_NUMERIC(T, unsigned int)
650  FB_CONVERT_ENTRY_NUMERIC(T, long)
651  FB_CONVERT_ENTRY_NUMERIC(T, unsigned long)
652  FB_CONVERT_ENTRY_NUMERIC(T, long long)
653  FB_CONVERT_ENTRY_NUMERIC(T, unsigned long long)
654  FB_CONVERT_ENTRY_NUMERIC(T, float)
655  FB_CONVERT_ENTRY_NUMERIC(T, double)
656  FB_CONVERT_ENTRY_COMPLEX_BEGIN(bool, bval);
657  // we handle bool here specifically because the numeric_cast produces warnings
658  return static_cast<T>(bval ? 1 : 0);
659  FB_CONVERT_ENTRY_COMPLEX_END();
660  FB_CONVERT_ENTRY_FROM_STRING(T, std::string)
661  FB_CONVERT_ENTRY_FROM_WSTRING(T, std::wstring)
662  FB_END_CONVERT_MAP(T)
663  }
664  }
665  }
666  template<typename T>
667  const T variant::convert_cast() const
668  {
669  return variant_detail::conversion::convert_variant(*this, variant_detail::conversion::type_spec<T>());
670  }
671  template <class T>
672  variant make_variant(T t)
673  {
674  // If you got an error on this line, you are trying to assign an unsupported type to
675  // FB::variant! If you're certain you want to do this then you should use the constructor
676  // or assign method that takes a bool. e.g.: variant tmp(myWeirdType, true);
677  return variant_detail::conversion::make_variant(t);
678  }
679 }
680 
681 #ifdef _WIN32
682 #pragma warning(pop)
683 #endif
684 
685 #undef FB_BEGIN_CONVERT_MAP
686 #undef FB_END_CONVERT_MAP
687 #undef FB_END_CONVERT_MAP_NO_THROW
688 #undef FB_CONVERT_ENTRY_SIMPLE
689 #undef FB_CONVERT_ENTRY_NUMERIC
690 #undef FB_CONVERT_ENTRY_TO_STRING
691 #undef FB_CONVERT_ENTRY_TO_WSTRING
692 #undef FB_CONVERT_ENTRY_FROM_STRING
693 #undef FB_CONVERT_ENTRY_FROM_STRING_TYPE
694 #undef FB_CONVERT_ENTRY_COMPLEX_BEGIN
695 #undef FB_CONVERT_ENTRY_COMPLEX_END
696 
697 #endif // FB_VARIANT_H
698 
const std::type_info & get_type() const
Gets the type of the value stored in variant.
Definition: variant.h:307
variant & assign(const T &x, bool)
Assigns a value of arbitrary type.
Definition: variant.h:271
const T convert_cast() const
Converts the stored value to the requested type if possible and returns the resulting value...
Definition: variant.h:667
Accepts any datatype, used in all interactions with javascript. Provides tools for getting back out t...
Definition: variant.h:198
boost::weak_ptr< FB::JSAPI > JSAPIWeakPtr
Defines an alias for a JSAPI weak_ptr (you should never use a JSAPI* directly)
Definition: APITypes.h:88
variant(const T &x)
Templated constructor to allow any arbitrary type.
Definition: variant.h:214
Thrown when variant::cast<type> or variant::convert_cast<type> fails because the type of the value st...
Definition: variant.h:133
variant & assign(const variant &x)
Assigns a new value from another variant.
Definition: variant.h:236
std::string wstring_to_utf8(const std::wstring &src)
Accepts a std::wstring and returns a UTF8-encoded std::string.
Definition: utf8_tools.cpp:37
variant()
Default constructor initializes the variant to an empty value.
Definition: variant.h:223
T cast() const
returns the value cast as the given type; throws bad_variant_type if that type is not the type of the...
Definition: variant.h:370
bool empty() const
Returns true if the variant is empty (has not been assigned a value or has been reset) ...
Definition: variant.h:403
void reset()
Frees any value assigned and resets the variant to empty state.
Definition: variant.h:416
bool is_of_type() const
Query if this object is of a particular type.
Definition: variant.h:355
std::wstring utf8_to_wstring(const std::string &src)
Accepts a UTF8-encoded std::string and returns a std::wstring.
Definition: utf8_tools.cpp:50