variant.hpp 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. /*
  2. Copyright 2005-2007 Adobe Systems Incorporated
  3. Use, modification and distribution are subject to the Boost Software License,
  4. Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  5. http://www.boost.org/LICENSE_1_0.txt).
  6. See http://opensource.adobe.com/gil for most recent version including documentation.
  7. */
  8. /*************************************************************************************************/
  9. #ifndef GIL_DYNAMICIMAGE_VARIANT_HPP
  10. #define GIL_DYNAMICIMAGE_VARIANT_HPP
  11. ////////////////////////////////////////////////////////////////////////////////////////
  12. /// \file
  13. /// \brief Support for run-time instantiated types
  14. /// \author Lubomir Bourdev and Hailin Jin \n
  15. /// Adobe Systems Incorporated
  16. /// \date 2005-2007 \n Last updated on September 18, 2007
  17. ///
  18. ////////////////////////////////////////////////////////////////////////////////////////
  19. #include "../../gil_config.hpp"
  20. #include "../../utilities.hpp"
  21. #include <cstddef>
  22. #include <cassert>
  23. #include <algorithm>
  24. #include <typeinfo>
  25. #include <boost/bind.hpp>
  26. #include <boost/utility/enable_if.hpp>
  27. #include <boost/mpl/bool.hpp>
  28. #include <boost/mpl/transform.hpp>
  29. #include <boost/mpl/size.hpp>
  30. #include <boost/mpl/sizeof.hpp>
  31. #include <boost/mpl/max.hpp>
  32. #include <boost/mpl/at.hpp>
  33. #include <boost/mpl/fold.hpp>
  34. namespace boost { namespace gil {
  35. namespace detail {
  36. template <typename Types, typename T> struct type_to_index;
  37. template <typename Op, typename T> struct reduce;
  38. struct destructor_op {
  39. typedef void result_type;
  40. template <typename T> result_type operator()(const T& t) const { t.~T(); }
  41. };
  42. template <typename T, typename Bits> void copy_construct_in_place(const T& t, Bits& bits);
  43. template <typename Bits> struct copy_construct_in_place_fn;
  44. template <typename Types> struct type_to_index_fn;
  45. }
  46. /**
  47. \brief Represents a concrete instance of a run-time specified type from a set of types
  48. \class variant
  49. \ingroup Variant
  50. A concept is typically modeled by a collection of different types. They may be instantiations
  51. of a templated type with different template parameters or even completely unrelated types.
  52. We call the type with which the concept is instantiated in a given place in the code "the concrete type".
  53. The concrete type must be chosen at compile time, which sometimes is a severe limitation.
  54. Consider, for example, having an image concept modeled by an image class templated over the color space.
  55. It would be difficult to write a function that reads an image from file preserving its native color space, since the
  56. type of the return value is only available at run time. It would be difficult to store images of different color
  57. spaces in the same container or apply operations on them uniformly.
  58. The variant class addresses this deficiency. It allows for run-time instantiation of a class from a given set of allowed classes
  59. specified at compile time. For example, the set of allowed classes may include 8-bit and 16-bit RGB and CMYK images. Such a variant
  60. can be constructed with rgb8_image_t and then assigned a cmyk16_image_t.
  61. The variant has a templated constructor, which allows us to construct it with any concrete type instantiation. It can also perform a generic
  62. operation on the concrete type via a call to apply_operation. The operation must be provided as a function object whose application
  63. operator has a single parameter which can be instantiated with any of the allowed types of the variant.
  64. variant breaks down the instantiated type into a non-templated underlying base type and a unique instantiation
  65. type identifier. In the most common implementation the concrete instantiation in stored 'in-place' - in 'bits_t'.
  66. bits_t contains sufficient space to fit the largest of the instantiated objects.
  67. GIL's variant is similar to boost::variant in spirit (hence we borrow the name from there) but it differs in several ways from the current boost
  68. implementation. Most notably, it does not take a variable number of template parameters but a single parameter defining the type enumeration. As
  69. such it can be used more effectively in generic code.
  70. The Types parameter specifies the set of allowable types. It models MPL Random Access Container
  71. */
  72. template <typename Types> // models MPL Random Access Container
  73. class variant {
  74. // size in bytes of the largest type in Types
  75. static const std::size_t MAX_SIZE = mpl::fold<Types, mpl::size_t<0>, mpl::max<mpl::_1, mpl::sizeof_<mpl::_2> > >::type::value;
  76. static const std::size_t NUM_TYPES = mpl::size<Types>::value;
  77. public:
  78. typedef Types types_t;
  79. typedef struct { char data[MAX_SIZE]; } base_t; // empty space equal to the size of the largest type in Types
  80. // Default constructor - default construct the first type
  81. variant() : _index(0) { new(&_bits) typename mpl::at_c<Types,0>::type(); }
  82. virtual ~variant() { apply_operation(*this, detail::destructor_op()); }
  83. // Throws std::bad_cast if T is not in Types
  84. template <typename T> explicit variant(const T& obj){ _index=type_id<T>(); if (_index==NUM_TYPES) throw std::bad_cast(); detail::copy_construct_in_place(obj, _bits); }
  85. template <typename Types2> explicit variant(const variant<Types2>& obj) : _index(apply_operation(obj,detail::type_to_index_fn<Types>())) {
  86. if (_index==NUM_TYPES) throw std::bad_cast();
  87. apply_operation(obj, detail::copy_construct_in_place_fn<base_t>(_bits));
  88. }
  89. // When doSwap is true, swaps obj with the contents of the variant. obj will contain default-constructed instance after the call
  90. template <typename T> explicit variant(T& obj, bool do_swap);
  91. template <typename T> variant& operator=(const T& obj) { variant tmp(obj); swap(*this,tmp); return *this; }
  92. variant& operator=(const variant& v) { variant tmp(v ); swap(*this,tmp); return *this; }
  93. variant(const variant& v) : _index(v._index) { apply_operation(v, detail::copy_construct_in_place_fn<base_t>(_bits)); }
  94. template <typename T> void move_in(T& obj) { variant tmp(obj, true); swap(*this,tmp); }
  95. template <typename TS> friend bool operator==(const variant<TS>& x, const variant<TS>& y);
  96. template <typename TS> friend bool operator!=(const variant<TS>& x, const variant<TS>& y);
  97. template <typename T> static bool has_type() { return type_id<T>()!=NUM_TYPES; }
  98. template <typename T> const T& _dynamic_cast() const { if (!current_type_is<T>()) throw std::bad_cast(); return *gil_reinterpret_cast_c<const T*>(&_bits); }
  99. template <typename T> T& _dynamic_cast() { if (!current_type_is<T>()) throw std::bad_cast(); return *gil_reinterpret_cast < T*>(&_bits); }
  100. template <typename T> bool current_type_is() const { return type_id<T>()==_index; }
  101. base_t bits() const { return _bits; }
  102. std::size_t index() const { return _index; }
  103. private:
  104. template <typename T> static std::size_t type_id() { return detail::type_to_index<Types,T>::value; }
  105. template <typename Cs> friend void swap(variant<Cs>& x, variant<Cs>& y);
  106. template <typename Types2, typename UnaryOp> friend typename UnaryOp::result_type apply_operation(variant<Types2>& var, UnaryOp op);
  107. template <typename Types2, typename UnaryOp> friend typename UnaryOp::result_type apply_operation(const variant<Types2>& var, UnaryOp op);
  108. template <typename Types1, typename Types2, typename BinaryOp> friend typename BinaryOp::result_type apply_operation(const variant<Types1>& arg1, const variant<Types2>& arg2, BinaryOp op);
  109. base_t _bits;
  110. std::size_t _index;
  111. };
  112. namespace detail {
  113. template <typename T, typename Bits>
  114. void copy_construct_in_place(const T& t, Bits& bits) {
  115. T& b=*gil_reinterpret_cast<T*>(&bits);
  116. new(&b)T(t); // default-construct
  117. }
  118. template <typename Bits>
  119. struct copy_construct_in_place_fn {
  120. typedef void result_type;
  121. Bits& _dst;
  122. copy_construct_in_place_fn(Bits& dst) : _dst(dst) {}
  123. template <typename T> void operator()(const T& src) const { copy_construct_in_place(src,_dst); }
  124. };
  125. template <typename Bits>
  126. struct equal_to_fn {
  127. const Bits& _dst;
  128. equal_to_fn(const Bits& dst) : _dst(dst) {}
  129. typedef bool result_type;
  130. template <typename T> result_type operator()(const T& x) const {
  131. return x==*gil_reinterpret_cast_c<const T*>(&_dst);
  132. }
  133. };
  134. template <typename Types>
  135. struct type_to_index_fn {
  136. typedef std::size_t result_type;
  137. template <typename T> result_type operator()(const T&) const { return detail::type_to_index<Types,T>::value; }
  138. };
  139. }
  140. // When doSwap is true, swaps obj with the contents of the variant. obj will contain default-constructed instance after the call
  141. template <typename Types>
  142. template <typename T> variant<Types>::variant(T& obj, bool do_swap) {
  143. _index=type_id<T>();
  144. if (_index==NUM_TYPES) throw std::bad_cast();
  145. if (do_swap) {
  146. new(&_bits) T(); // default construct
  147. swap(obj, *gil_reinterpret_cast<T*>(&_bits));
  148. } else
  149. detail::copy_construct_in_place(const_cast<const T&>(obj), _bits);
  150. }
  151. template <typename Types>
  152. void swap(variant<Types>& x, variant<Types>& y) {
  153. std::swap(x._bits,y._bits);
  154. std::swap(x._index, y._index);
  155. }
  156. template <typename Types>
  157. inline bool operator==(const variant<Types>& x, const variant<Types>& y) {
  158. return x._index==y._index && apply_operation(x,detail::equal_to_fn<typename variant<Types>::base_t>(y._bits));
  159. }
  160. template <typename C>
  161. inline bool operator!=(const variant<C>& x, const variant<C>& y) {
  162. return !(x==y);
  163. }
  164. } } // namespace boost::gil
  165. #endif