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