Chris@16: /* Chris@16: Copyright 2005-2007 Adobe Systems Incorporated Chris@16: Chris@16: Use, modification and distribution are subject to the Boost Software License, Chris@16: Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at Chris@16: http://www.boost.org/LICENSE_1_0.txt). Chris@16: Chris@16: See http://opensource.adobe.com/gil for most recent version including documentation. Chris@16: */ Chris@16: Chris@16: /*************************************************************************************************/ Chris@16: Chris@16: #ifndef GIL_DYNAMICIMAGE_VARIANT_HPP Chris@16: #define GIL_DYNAMICIMAGE_VARIANT_HPP Chris@16: Chris@16: //////////////////////////////////////////////////////////////////////////////////////// Chris@16: /// \file Chris@16: /// \brief Support for run-time instantiated types Chris@16: /// \author Lubomir Bourdev and Hailin Jin \n Chris@16: /// Adobe Systems Incorporated Chris@16: /// \date 2005-2007 \n Last updated on September 18, 2007 Chris@16: /// Chris@16: //////////////////////////////////////////////////////////////////////////////////////// Chris@16: Chris@16: #include "../../gil_config.hpp" Chris@16: #include "../../utilities.hpp" Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: Chris@16: namespace boost { namespace gil { Chris@16: Chris@16: namespace detail { Chris@16: template struct type_to_index; Chris@16: template struct reduce; Chris@16: struct destructor_op { Chris@16: typedef void result_type; Chris@16: template result_type operator()(const T& t) const { t.~T(); } Chris@16: }; Chris@16: template void copy_construct_in_place(const T& t, Bits& bits); Chris@16: template struct copy_construct_in_place_fn; Chris@16: } Chris@16: /** Chris@16: \brief Represents a concrete instance of a run-time specified type from a set of types Chris@16: \class variant Chris@16: \ingroup Variant Chris@16: Chris@16: A concept is typically modeled by a collection of different types. They may be instantiations Chris@16: of a templated type with different template parameters or even completely unrelated types. Chris@16: Chris@16: We call the type with which the concept is instantiated in a given place in the code "the concrete type". Chris@16: The concrete type must be chosen at compile time, which sometimes is a severe limitation. Chris@16: Consider, for example, having an image concept modeled by an image class templated over the color space. Chris@16: It would be difficult to write a function that reads an image from file preserving its native color space, since the Chris@16: type of the return value is only available at run time. It would be difficult to store images of different color Chris@16: spaces in the same container or apply operations on them uniformly. Chris@16: Chris@16: The variant class addresses this deficiency. It allows for run-time instantiation of a class from a given set of allowed classes Chris@16: 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 Chris@16: can be constructed with rgb8_image_t and then assigned a cmyk16_image_t. Chris@16: Chris@16: The variant has a templated constructor, which allows us to construct it with any concrete type instantiation. It can also perform a generic Chris@16: operation on the concrete type via a call to apply_operation. The operation must be provided as a function object whose application Chris@16: operator has a single parameter which can be instantiated with any of the allowed types of the variant. Chris@16: Chris@16: variant breaks down the instantiated type into a non-templated underlying base type and a unique instantiation Chris@16: type identifier. In the most common implementation the concrete instantiation in stored 'in-place' - in 'bits_t'. Chris@16: bits_t contains sufficient space to fit the largest of the instantiated objects. Chris@16: Chris@16: 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 Chris@16: implementation. Most notably, it does not take a variable number of template parameters but a single parameter defining the type enumeration. As Chris@16: such it can be used more effectively in generic code. Chris@16: Chris@16: The Types parameter specifies the set of allowable types. It models MPL Random Access Container Chris@16: */ Chris@16: Chris@16: template // models MPL Random Access Container Chris@16: class variant { Chris@16: // size in bytes of the largest type in Types Chris@16: static const std::size_t MAX_SIZE = mpl::fold, mpl::max > >::type::value; Chris@16: static const std::size_t NUM_TYPES = mpl::size::value; Chris@16: public: Chris@16: typedef Types types_t; Chris@16: Chris@16: typedef struct { char data[MAX_SIZE]; } base_t; // empty space equal to the size of the largest type in Types Chris@16: Chris@16: // Default constructor - default construct the first type Chris@16: variant() : _index(0) { new(&_bits) typename mpl::at_c::type(); } Chris@16: virtual ~variant() { apply_operation(*this, detail::destructor_op()); } Chris@16: Chris@16: // Throws std::bad_cast if T is not in Types Chris@16: template explicit variant(const T& obj){ _index=type_id(); if (_index==NUM_TYPES) throw std::bad_cast(); detail::copy_construct_in_place(obj, _bits); } Chris@16: Chris@16: // When doSwap is true, swaps obj with the contents of the variant. obj will contain default-constructed instance after the call Chris@16: template explicit variant(T& obj, bool do_swap); Chris@16: Chris@16: template variant& operator=(const T& obj) { variant tmp(obj); swap(*this,tmp); return *this; } Chris@16: variant& operator=(const variant& v) { variant tmp(v ); swap(*this,tmp); return *this; } Chris@16: Chris@16: variant(const variant& v) : _index(v._index) { apply_operation(v, detail::copy_construct_in_place_fn(_bits)); } Chris@16: template void move_in(T& obj) { variant tmp(obj, true); swap(*this,tmp); } Chris@16: Chris@16: template friend bool operator==(const variant& x, const variant& y); Chris@16: template friend bool operator!=(const variant& x, const variant& y); Chris@16: Chris@16: template static bool has_type() { return type_id()!=NUM_TYPES; } Chris@16: Chris@16: template const T& _dynamic_cast() const { if (!current_type_is()) throw std::bad_cast(); return *gil_reinterpret_cast_c(&_bits); } Chris@16: template T& _dynamic_cast() { if (!current_type_is()) throw std::bad_cast(); return *gil_reinterpret_cast < T*>(&_bits); } Chris@16: Chris@16: template bool current_type_is() const { return type_id()==_index; } Chris@16: Chris@16: base_t bits() const { return _bits; } Chris@16: std::size_t index() const { return _index; } Chris@16: Chris@16: private: Chris@16: template static std::size_t type_id() { return detail::type_to_index::value; } Chris@16: Chris@16: template friend void swap(variant& x, variant& y); Chris@16: template friend typename UnaryOp::result_type apply_operation(variant& var, UnaryOp op); Chris@16: template friend typename UnaryOp::result_type apply_operation(const variant& var, UnaryOp op); Chris@16: template friend typename BinaryOp::result_type apply_operation(const variant& arg1, const variant& arg2, BinaryOp op); Chris@16: Chris@16: base_t _bits; Chris@16: std::size_t _index; Chris@16: }; Chris@16: Chris@16: namespace detail { Chris@16: Chris@16: template Chris@16: void copy_construct_in_place(const T& t, Bits& bits) { Chris@16: T& b=*gil_reinterpret_cast(&bits); Chris@16: new(&b)T(t); // default-construct Chris@16: } Chris@16: Chris@16: template Chris@16: struct copy_construct_in_place_fn { Chris@16: typedef void result_type; Chris@16: Bits& _dst; Chris@16: copy_construct_in_place_fn(Bits& dst) : _dst(dst) {} Chris@16: Chris@16: template void operator()(const T& src) const { copy_construct_in_place(src,_dst); } Chris@16: }; Chris@16: Chris@16: template Chris@16: struct equal_to_fn { Chris@16: const Bits& _dst; Chris@16: equal_to_fn(const Bits& dst) : _dst(dst) {} Chris@16: Chris@16: typedef bool result_type; Chris@16: template result_type operator()(const T& x) const { Chris@16: return x==*gil_reinterpret_cast_c(&_dst); Chris@16: } Chris@16: }; Chris@16: } Chris@16: Chris@16: // When doSwap is true, swaps obj with the contents of the variant. obj will contain default-constructed instance after the call Chris@16: template Chris@16: template variant::variant(T& obj, bool do_swap) { Chris@16: _index=type_id(); Chris@16: if (_index==NUM_TYPES) throw std::bad_cast(); Chris@16: Chris@16: if (do_swap) { Chris@16: new(&_bits) T(); // default construct Chris@16: swap(obj, *gil_reinterpret_cast(&_bits)); Chris@16: } else Chris@16: detail::copy_construct_in_place(const_cast(obj), _bits); Chris@16: } Chris@16: Chris@16: template Chris@16: void swap(variant& x, variant& y) { Chris@16: std::swap(x._bits,y._bits); Chris@16: std::swap(x._index, y._index); Chris@16: } Chris@16: Chris@16: template Chris@16: inline bool operator==(const variant& x, const variant& y) { Chris@16: return x._index==y._index && apply_operation(x,detail::equal_to_fn::base_t>(y._bits)); Chris@16: } Chris@16: Chris@16: template Chris@16: inline bool operator!=(const variant& x, const variant& y) { Chris@16: return !(x==y); Chris@16: } Chris@16: Chris@16: } } // namespace boost::gil Chris@16: Chris@16: #endif