[GIL] does gil::variant<mpl::vector3<A, B, C> > constructor accept instances of gil::variant<mpl::vector2<A, B> > ?
Hi ! the question is in the subject, but I can elaborate a bit. I have the set of all views supported by my application, defined as : typedef mpl::vector9< gray8_view_t , rgb8_view_t, bgr8_view_t, rgba8_view_t, bgra8_view_t , rgb32f_view_t, bgr32f_view_t, rgba32f_view_t, bgra32f_view_t > gil_all_views_t; typedef any_image_view<gil_all_views_t> any_view; Then I have a function, reading a file and returning an any_image_view, of a different type : typedef boost::mpl::vector3<boost::gil::gray8_view_t, boost::gil::bgr8_view_t, boost::gil::bgra8_view_t> tga_views_t; typedef boost::gil::any_image_view<tga_views_t> any_tga_view; The ' tga_views_t' is a subset of 'gil_all_views_t'. It seems that I cannot construct an 'any_view' instance, from an 'any_tga_view' ; I receive a bad_cast exception. Is there an existing function or construct I could use for this ? If I'm not mistaken, boost::variant (not the gil one) allow such constructs : it extracts the type of the value contained in the 'foreign' variant, and assigns this value to the new variant. Anyone could tell me what is the rationale behind having a special variant for boost::gil ? performance ? Wouldn't it be easier to replace it by boost::variant ? Thanks is advance, cheers, Nicolas.
Nicolas Lelong wrote:
does gil::variant<mpl::vector3<A, B, C> > constructor accept instances of gil::variant<mpl::vector2<A, B> > ? ... Anyone could tell me what is the rationale behind having a special variant for boost::gil ? performance ? Wouldn't it be easier to replace it by boost::variant ?
Hi Nicolas, It was not possible to assign and copy construct between two gil::variants containing different subsets of types. I just submitted and update that makes this possible so you should sync to trunk. As for why we don’t use boost::variant, I recall that I was able to switch to boost::variant at some point (it wasn’t that hard) but there was a severe compile time penalty. Another reason is that gil::variant takes a single type, which is an MPL random access container of types, whereas boost::variant takes the types directly. The former makes a lot of operations easier. Lubomir
AMDG Lubomir Bourdev wrote:
Another reason is that gil::variant takes a single type, which is an MPL random access container of types, whereas boost::variant takes the types directly. The former makes a lot of operations easier.
Boost.Variant handles both. (See make_variant_over) In Christ, Steven Watanabe
On 05/10/10 20:33, Lubomir Bourdev wrote: [snip]
As for why we don’t use boost::variant, I recall that I was able to switch to boost::variant at some point (it wasn’t that hard) but there was a severe compile time penalty. [snip] Did you figure out the reason for the compile time penalty. Looking at boost/gil/extension/dynamic_image/variant.hpp around line 94, I see:
typedef struct { char data[MAX_SIZE]; } base_t; // empty space //equal to the size of the largest type in Types So, I assume no alignment calculations are made. Would the alignment calculations be the main cause of the compile time penalty? The alignment calculations I'm talking about are those done by make_storage in boost/variant/variant.hpp at around line 218. I assume gil doesn't need any alignment calculations because all alignments required by the gil variant components are 1. Is that right? TIA. -regards, Larry
These were experiments I did five years ago so I don't remember all the details, but my recollection was that if I used boost::variant with many types (a dozen or more gil::image or gil::image_view types) into more elaborate computations, especially binary functions (which require evaluating every possible pair of types), it would take minutes to compile and it would sometimes fail to compile at all. That prompted me to create my own variant class which, at the time at least, had much better scalability and compile performance. It is possible that gil::variant does not have all the features of boost::variant but it was serving its purpose well. There might have been other reasons to use gil::variant instead of boost::variant (some generic operations were easier) but I don't recall the details. I am all for code reuse where it makes sense. In this case gil::variant is very small and we are not duplicating much code. If someone feels strongly about using boost::variant instead and is willing to take a stab at redoing the experiments, and they show acceptable performance (things may have changed over the past 5 years), I would be ok with switching to boost::variant. Note that gil::variant is not typically used directly. It is a base class for gil::any_image and gil::any_image_view. You would need to deal with gil::variant only if you are a pretty advanced GIL user. Lubomir On 5/11/10 5:05 AM, "Larry Evans" <cppljevans@suddenlink.net> wrote:
On 05/10/10 20:33, Lubomir Bourdev wrote: [snip]
As for why we don¹t use boost::variant, I recall that I was able to switch to boost::variant at some point (it wasn¹t that hard) but there was a severe compile time penalty. [snip] Did you figure out the reason for the compile time penalty. Looking at boost/gil/extension/dynamic_image/variant.hpp around line 94, I see:
typedef struct { char data[MAX_SIZE]; } base_t; // empty space //equal to the size of the largest type in Types
So, I assume no alignment calculations are made. Would the alignment calculations be the main cause of the compile time penalty? The alignment calculations I'm talking about are those done by make_storage in boost/variant/variant.hpp at around line 218. I assume gil doesn't need any alignment calculations because all alignments required by the gil variant components are 1. Is that right?
TIA.
-regards, Larry
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
It was not possible to assign and copy construct between two gil::variants containing different subsets of types. I just submitted and update that makes this possible so you should sync to
Lubomir, trunk. thanks for your quick reaction ! I am all for code reuse where it makes sense. In this case gil::variant is
very small and we are not duplicating much code. If someone feels strongly about using boost::variant instead and is willing to take a stab at redoing the experiments, and they show acceptable performance (things may have changed over the past 5 years), I would be ok with switching to boost::variant.
I'm no template metaprogramming guru, but I tried a quick experiment on my code base. Don't know if its a valid approach. My current image library uses gil, any_image_view, and some binary functions among them, it currently builds in about 33 seconds, using MSVC8 SP1. I made a quick and dirty test, simply tried to change gil variant.hpp to use boost::variant, this lead to something like that (incomplete exemple, I did not look to be exhaustive) for class boost::gil::variant : template <typename Types> // models MPL Random Access Container class variant : public boost::make_variant_over<Types>::type { public: typedef typename boost::make_variant_over<Types>::type parent_t; variant() : parent_t() {} template <typename T> explicit variant(const T& obj) : parent_t(obj) {} variant(const variant& obj) : parent_t((const parent_t&)obj) {} template <typename T> variant& operator=(const T& obj) { parent_t::operator=(obj); return *this; } variant& operator=(const variant& obj) { parent_t::operator=((const parent_t&)obj); return *this; } template <typename T> const T& _dynamic_cast() const { return get<const T&>(*this); } template <typename T> T& _dynamic_cast() { return get<T&>(*this); } }; I also changed apply_operation.hpp to use boost::variant : /// \ingroup Variant /// \brief Invokes a generic mutable operation (represented as a unary function object) on a variant template <typename Types, typename UnaryOp> GIL_FORCEINLINE typename UnaryOp::result_type apply_operation(boost::variant<Types>& arg, UnaryOp op) { return apply_visitor(op, arg); } /// \ingroup Variant /// \brief Invokes a generic constant operation (represented as a unary function object) on a variant template <typename Types, typename UnaryOp> GIL_FORCEINLINE typename UnaryOp::result_type apply_operation(const boost::variant<Types>& arg, UnaryOp op) { return apply_visitor(op, arg); } /// \ingroup Variant /// \brief Invokes a generic constant operation (represented as a binary function object) on two variants template <typename Types1, typename Types2, typename BinaryOp> GIL_FORCEINLINE typename BinaryOp::result_type apply_operation(const boost::variant<Types1>& arg1, const boost::variant<Types2>& arg2, BinaryOp op) { return apply_visitor(op, arg1, arg2); } I was able to compile most of my imaging lib this way, I had some compile error I could not solve, related to ambiguous conversions, so I removed some of my lib code. The compile time went up to 52 seconds, almost 57% more. Of course, this may be a bad test, as I added an indirection layer, but I did not want to break my code related to 'boost::gil::apply_operation'.
Note that gil::variant is not typically used directly. It is a base class for gil::any_image and gil::any_image_view. You would need to deal with gil::variant only if you are a pretty advanced GIL user.
True, but it's nevertheless confusing to use both variant types in the same code base ; they don't have the same interface - perhaps boost::gil::variant could have a subset of boost::variant interface ? for instance, an 'empty()' method would be convenient for boost::gil::variant. Cheers, Nicolas
participants (4)
-
Larry Evans
-
Lubomir Bourdev
-
Nicolas Lelong
-
Steven Watanabe