Boost logo

Boost Users :

Subject: Re: [Boost-users] compilation problem and a couple ofboost::variant questions
From: Jeffrey Lee Hellrung, Jr. (jeffrey.hellrung_at_[hidden])
Date: 2012-07-11 17:39:23


[Please avoid top-posting;
http://www.boost.org/community/policy.html#quoting]

On Wed, Jul 11, 2012 at 2:10 PM, Petros <pmamales_at_[hidden]> wrote:

> Jeff,
> Thank you very much for responding.
> I tried containment, instead of inheritance, as you suggest, getting the
> same error messages.
>

Ummm...are you sure? The following compiles fine for me on MSVC9 (note
copious additional fixes which did not include fixing the template
parameter shadowing):

--------
#include <boost/variant.hpp>
using boost::variant;

struct Zero1{ double operator[]( const size_t i ) const { return 0L ; } };
struct One1{ double operator[]( const size_t i ) const { return 1L ; } };
struct Zero2{ double operator[]( const size_t i ) const { return 0L ; } };
struct One2{ double operator[]( const size_t i ) const { return 1L ; } };
struct Vector1{ double operator[]( const size_t i ) const {return double(
i ) ; } };
struct Vector2{ double operator[]( const size_t i ) const {return double(
2*i ) ; } };
struct
    subscript_operator:public boost::static_visitor<double>{
        const size_t i_;
        subscript_operator( const size_t i ):i_(i){}

        template<typename _v>
        double operator()( _v const & v ) const {
            //return v[i] ;
            return v[i_] ;
        }
};

template <typename _V, typename _Z, typename _U>
class VariantT
    //:public variant< _V, _Z, _U >
{
public:
    typedef typename _Z zero_type ;
    typedef typename _U unit_type ;

    typedef typename VariantT<_V, _Z, _U> self_type ;
    typedef typename variant< _V, _Z, _U > variant_type ;

    template < typename _V>
    explicit VariantT( _V const & v )
        //:variant_type(v)
        :v_(v)
    {}
    VariantT()
    {}
    //VariantT( self_type const & v )
    // :variant_type(static_cast<const variant_type&>(v))
    //{}
    template < typename _V>
    VariantT & operator = ( _V const & v ) {
        //*static_cast<variant_type*>(this) = static_cast<const
variant_type&>(v);
        v_ = v;
        return(*this);
    }
    //VariantT & operator = ( self_type const & v ) {
    // *static_cast<variant_type*>(this) = v;
    // return(*this);
    //}

    double operator[]( const size_t i ) const {
        //boost::apply_visitor( subscript_operator(i) , *this ) ;
        return boost::apply_visitor( subscript_operator(i), v_ ) ;
    }

    //bool isZero() const { return which() == 1 ; }
    bool isZero() const { return v_.which() == 1 ; }
    //bool isUnit() const { return which() == 2 ; }
    bool isUnit() const { return v_.which() == 2 ; }

    template< class Visitor >
    typename Visitor::result_type
    apply_visitor(Visitor visitor) const
    { return boost::apply_visitor(visitor, v_); }

private:
    variant_type v_;
};

template < typename _V1, typename _V2 >
class MultVarVar{
public:
    MultVarVar( _V1 const & v1, _V2 const & v2 )
        :v1_(v1),v2_(v2)
    {}
    ~MultVarVar()
    {}

    typedef typename _V1::zero_type zero_type ;
    typedef typename _V1::unit_type unit_type ;

    double operator[]( const size_t i ) const {
        return
            //boost::apply_visitor ( elementwise_visitor( i ), v1, v2 ) ;
            boost::apply_visitor ( elementwise_visitor( i ), v1_, v2_ ) ;
    }

private:
    _V1 const & v1_ ;
    _V2 const & v2_ ;

    struct
        elementwise_visitor
        : public boost::static_visitor< double > {
            const size_t & i_;
    public:
        elementwise_visitor( const size_t & i )
            :i_(i)
        {}
        template <typename _V1, typename _V2 >
        double operator()( _V1 const & v1, _V2 const & v2) const {
            return v1[i_] * v2[i_] ;
        }
    };
};

template < typename _V1, typename _V2 >
class MultVarVarResult
    :public variant<typename MultVarVar<_V1, _V2>, typename
MultVarVar<_V1,_V2>::zero_type, typename MultVarVar<_V1,_V2>::unit_type,
_V1, _V2 > {
public:
    typedef typename variant<
        typename MultVarVar<_V1, _V2 > ,
        typename MultVarVar<_V1,_V2>::zero_type,
        typename MultVarVar<_V1,_V2>::unit_type,
        _V1,
        _V2 > variant_type;
    typedef typename MultVarVarResult<_V1, _V2> self_type ;

    typedef typename _V1::zero_type zero_type ;
    typedef typename _V1::unit_type unit_type ;

    template<typename _V>
    /*explicit*/ MultVarVarResult( _V const & v )
        :variant_type(v)
    {}
    MultVarVarResult( self_type const & v )
        :variant_type(static_cast<const variant_type&>(v))
    {}
    template < typename _V>
    MultVarVarResult & operator = ( _V const & v ) {
        *static_cast<variant_type*>(this) = static_cast<const
variant_type&>(v);
        return(*this);
    }
    MultVarVarResult & operator = ( self_type const & v ) {
        *static_cast<variant_type*>(this) = v;
        return(*this);
    }

    double operator[]( const size_t i ) const {
        //boost::apply_visitor( subscript_operator(i), *this ) ;
        return boost::apply_visitor( subscript_operator(i), *this ) ;
    }
private:
};

template <typename _V1, typename _V2>
MultVarVarResult<_V1, _V2> operator * ( _V1 const & v1, _V2 const & v2 ) {

    if ( v1.isZero() || v2.isZero() )
        return
            MultVarVarResult<_V1, _V2>( typename
MultVarVar<_V1,_V2>::zero_type() ) ;
    if ( v1.isUnit() )
        return
            MultVarVarResult<_V1, _V2>( v2 );
    if ( v2.isUnit() )
        return
            v1 ;
    else
        return
            MultVarVarResult<_V1, _V2>( MultVarVar<_V1, _V2>( v1, v2 ) ) ;
}

typedef VariantT< Vector1, Zero1, One1 > Variant1;
typedef VariantT< Vector2, Zero2, One2 > Variant2;

int main(){
    Variant1 v1;
    v1 = Zero1() ;
    Variant2 v2;
    v2 = One2() ;

    //auto x = v1 * v2 ;
    MultVarVarResult< Variant1, Variant2 > x = v1 * v2;

    return x[0] == double(0) ;
}
--------

> Tried to substitute the templated c/tors/assignment operators but w/out
> (good) result.
>

What do you mean?

 I believe that the real issue is that the c/tor in variant is templated.
>

The problem isn't the templatization per se, it's the unconstrainedness of
the templated constructor (IIRC) :/

> As far as the identical template arguments for variant, maybe a c/tor
> with the (0-based ?) type could be of use.
>

You mean the first type in the sequence of value types that is convertible
from the argument? That's possible, but I don't believe boost::variant
presently attempts to do that. You can always try!

> Imagine the amount of complexity that is generated, by trying to write
> template expressions where arguments
> may just happen to be the same (of course, I know nothing on the
> implementation details of variant – so I may well be waaaay off [image:
> Winking smile])
>

I don't think it's too difficult to write a metafunction that generates the
desired variant, and all the complexity would be isolated to that
metafunction.

 Finally, I am only interested about compile time “polymorphism”.
>

Hmmm...then why are you using variant?

> The real motivation of all this is that, I have expressions that can
> sometimes generate
> a unit/zero vector (like in a Monte-Carlo simulation) and want to
> maintain a uniform interface across expressions that generate this “vector”
> (hence the variant)
> -nothing terribly original [image: Winking smile])
>

Well, variant essentially erases the type of the object it's constructed
with, so you're effectively using runtime polymorphism (though of a more
controlled nature than boost::any or virtual functions). I don't know
enough about the particulars of your application based on your description
above to know if that's appropriate.

> So, do you think that this construct will be optimized away at compile
> time ( for me the variants can as well be no-re-assignable) ?
>

I still don't know what you want optimized away.

 Thank you very much for taking the trouble to look into it,
> Petros
>
> PS: google search indicated that this kind of problem was attributed to
> the msvc compiler misusing the base templated c/tors. However, the result
> is identical with the intel
> 12.1 compiler (not that it cannot suffer from the same issue).
>

It's a related but distinct issue in this case.

> *From:* Jeffrey Lee Hellrung, Jr. <jeffrey.hellrung_at_[hidden]>
> *Sent:* Wednesday, July 11, 2012 4:10 PM
> *To:* boost-users_at_[hidden]
> *Subject:* Re: [Boost-users] compilation problem and a couple
> ofboost::variant questions
>
> On Wed, Jul 11, 2012 at 11:32 AM, Petros <pmamales_at_[hidden]> wrote:
>
>> Hi,
>> I am trying to write some operator that multiplies two variants, each one
>> containing a vector a zero and a unit, where I am trying to
>> use the fact that I know the outcome for multiplying by 0 or by 1.
>> The code (very bare bones) below describes how this is done, by:
>> _ defining dummy structures of Variant-1, 2
>> _ defining a class that contains the instruction for multiplication
>> _ defining a class (derived from variant) that contains possible outcomes
>> of the multiplication
>> _ defining the operator
>> (only a few of the possible outcomes are shown)
>> Compiling with msvc2010 (intel has similar issues) on win7:
>> First here is the code:
>>
>> #include <boost/variant.hpp>
>> using boost::variant;
>>
>> struct Zero1{ double operator[]( const size_t i ) const { return 0L ; }
>> };
>> struct One1{ double operator[]( const size_t i ) const { return 1L ; }
>> };
>> struct Zero2{ double operator[]( const size_t i ) const { return 0L ; }
>> };
>> struct One2{ double operator[]( const size_t i ) const { return 1L ; }
>> };
>> struct Vector1{ double operator[]( const size_t i ) const {return
>> double( i ) ; } };
>> struct Vector2{ double operator[]( const size_t i ) const {return
>> double( 2*i ) ; } };
>> struct
>> subscript_operator:public boost::static_visitor<double>{
>> const size_t i_;
>> subscript_operator( const size_t i ):i_(i){}
>>
>> template<typename _v>
>> double operator()( _v const & v ) const {
>> return v[i] ;
>> }
>> };
>>
>>
>> template <typename _V, typename _Z, typename _U>
>> class VariantT
>> :public variant< _V, _Z, _U >
>>
>
> I believe the fact that VariantT<> inherits from boost::variant (combined
> with the structure of the boost::variant implementation) is the cause of
> the ambiguity in convert_construct referenced below. For example, the
> following minimal code produces the same problem:
>
> #include <boost/variant.hpp>
>
> struct X
> : boost::variant< int >
> { };
>
> void main()
> {
> X x;
> boost::variant<X> y(x);
> }
>
> I'll file a trac ticket against boost::variant; in the meantime, try
> making boost::variant a member object rather than a base object.
>
> [...snip remaining code...]
>
>> And here is one of the errors :
>>
>> boost_1_49_0\boost\variant\variant.hpp(1399): error C2666:
>> 'boost::variant<T0_,T1,T2,T3,T4>::convert_construct' : 2 overloads have
>> similar conversions
>> 1> with
>> 1> [
>> 1> T0_=MultVarVar<Variant1,Variant2>,
>> 1> T1=Zero1,
>> 1> T2=One1,
>> 1> T3=Variant1,
>> 1> T4=Variant2
>> 1> ]
>> 1>
>> c:\_petros\_otc\ext\boost_1_49_0\boost\variant\variant.hpp(1384): could be
>> 'void
>> boost::variant<T0_,T1,T2,T3,T4>::convert_construct<_V,_Z,_U,boost::detail::variant::void_,boost::detail::variant::void_,boost::detail::variant::void_,boost::detail::variant::void_,boost::detail::variant::void_,boost::detail::variant::void_,boost::detail::variant::void_,boost::detail::variant::void_,boost::detail::variant::void_,boost::detail::variant::void_,boost::detail::variant::void_,boost::detail::variant::void_,boost::detail::variant::void_,boost::detail::variant::void_,boost::detail::variant::void_,boost::detail::variant::void_,boost::detail::variant::void_>(const
>> boost::variant<_V,T1,T2> &,long)'
>> 1> with
>> 1> [
>> 1> T0_=MultVarVar<Variant1,Variant2>,
>> 1> T1=Zero1,
>> 1> T2=One1,
>> 1> T3=Variant1,
>> 1> T4=Variant2,
>> 1> _V=Vector1,
>> 1> _Z=Zero1,
>> 1> _U=One1
>> 1> ]
>> 1>
>> c:\_petros\_otc\ext\boost_1_49_0\boost\variant\variant.hpp(1315): or
>> 'void boost::variant<T0_,T1,T2,T3,T4>::convert_construct<const T>(T
>> &,int,boost::mpl::false_)'
>> 1> with
>> 1> [
>> 1> T0_=MultVarVar<Variant1,Variant2>,
>> 1> T1=Zero1,
>> 1> T2=One1,
>> 1> T3=Variant1,
>> 1> T4=Variant2,
>> 1> T=Variant1
>> 1> ]
>> 1> while trying to match the argument list '(const Variant1,
>> long)'
>> 1> c:\_petros\_otc\tests\variant\variant\main.cpp(120) : see
>> reference to function template instantiation
>> 'boost::variant<T0_,T1,T2,T3,T4>::variant<_V>(const T &)' being compiled
>> 1> with
>> 1> [
>> 1> T0_=MultVarVar<Variant1,Variant2>,
>> 1> T1=Zero1,
>> 1> T2=One1,
>> 1> T3=Variant1,
>> 1> T4=Variant2,
>> 1> _V=Variant1,
>> 1> T=Variant1
>> 1> ]
>> 1> c:\_petros\_otc\tests\variant\variant\main.cpp(151) : see
>> reference to function template instantiation
>> 'MultVarVarResult<_V1,_V2>::MultVarVarResult<_V1>(const _V &)' being
>> compiled
>> 1> with
>> 1> [
>> 1> _V1=Variant1,
>> 1> _V2=Variant2,
>> 1> _V=Variant1
>> 1> ]
>> 1>
>>
>> Any help on this will be greatly appreciated!
>> And the questions:
>> What happens if a variant contains two identical template arguments ?
>> when I create a (variant) object from one of the two identicals, how does
>> it know which template argument I need ?
>>
>
> It probably doesn't :) My guess is you'll get some compiler errors based
> on ambiguous overloads (distinct but similar to the errors above). I think
> your best bet is to preprocess the sequence of value types to remove
> duplicate types prior to generating your variant type. You can do this via
> Boost.MPL, e.g., the documentation for boost::mpl::set<> [1] has an example
> to this effect, and then you may use boost::make_variant_over [2].
>
>
>> Do I have to write the same code for the specialization of identical
>> _V1 and _V2?
>>
>
> That or complicate the generation of the variant type, as I've described
> above.
>
>
>> Also, am I correct to assume that all this syntax ( element-wise
>> static_visitor’s etc) will be optimized away at a release build ?
>>
>
> I'm not sure what you're looking to have optimized away. If you mean that
> the compiler can somehow deduce at compile time the runtime underlying type
> of the variant based on your code in main and somehow optimize away the
> runtime dispatching...I guess that's possible, but I wouldn't depend on it
> or care, as I can't think of a reason you'd use a boost::variant in such a
> scenario other than for testing. On the other hand, if it's impossible to
> know the runtime underlying type of the variant, then there's no way to get
> around the runtime dispatching.
>
> However, I would expect any decent compiler to collapse all statically
> known calls before and after the runtime dispatch, if that's what you're
> asking.
>
> HTH,
>
> - Jeff
>
> [1] http://www.boost.org/doc/libs/1_50_0/libs/mpl/doc/refmanual/set.html
> [2]
> http://www.boost.org/doc/libs/1_50_0/doc/html/variant/tutorial.html#variant.tutorial.over-sequence
>



Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net