Boost logo

Boost :

From: Yuval Ronen (ronen_yuval_at_[hidden])
Date: 2006-06-05 07:39:02


Hi Eric!
It's good to see that you've got back to the list. For a long time now,
there was no one to answer users' questions and comments, So I'm taking
this (rare :-) ) opportunity to present my wish list for Boost.Variant.

First of all, I'd like to say that I think it's a great library. I've
become addicted to it... However, there are some things I think are missing.

1. add operator !=

There's operator==, and adding operator!= would be both easy and useful.

2. apply_visitor should accept the visitor by value

std algorithms (such as for_each) accept their functors by value, not by
[const] reference. I think that visitors should be considered as
variant's equivalent to std functors, and thus be passed by value. This
will allow me to pass temporary visitor objects, which is a very common
use case, just as with std functors. It will also allow you to delete
the two versions of apply_visitor (one taking a reference, one taking a
const reference), and leave only one.

3. add support for non-copyable types, a-la Boost.Optional's in-place
factory

I found myself needing a variant of non-copyable types, and had to
resort to dynamic allocation to solve it. Luckily for me, it wasn't a
performance-critical code, so I could live with it. But of course it's
not optimal...

4. add more flavours of get()

Currently the get<T>() function has the semantics of "return current
value only if it is of type implicitly convertible to T". But there are
other usages, even more useful than this (and if there are other get()s,
the name of the original get() should be changed to, for example,
get_implicit).

One of them is "return current value only if it is exactly of type T
(using type_info)". The function will not compile if T is not any of the
variant's types. A possible name for it could be get_exact(), but of
course there might be better names.

Another usage I found useful is when all of the variant's types are
implicitly convertible to a common T (e.g, they all inherit from T).
This function will not compile if any of the variant's types can't be
implicitly convertible to T, but if it does compile, then it ensures it
can't fail in run-time (in contrast to get_implicit and get_exact, which
can fail in run-time). A possible name is get_common().

I've attached a file containing my implementation of get_exact and
get_common, in case you're interested. All the preprocessor magic there
is beyond my understanding, I just copied it from your code, and hope I
didn't make any mistakes...

5. support for reference types

I don't know if Boost.Variant currently supports reference types, nor am
I certain it should, but it's a possibility...
There was a debate about Boost.Optional's support for reference types,
which reached no clear conclusion. Maybe you're familiar with the
details, but if not, in short, the problem is with code such as

int a = ...;
int b = ...;
optional<int &> x; // x in initialized to 'empty'
x = a; // x is binded to a's reference
x = b; // is x rebinded to b's reference, or is a assigned the value in b?

The reason I bring it up here, is because I think it's relevant to
Boost.Variant as well. Variant of reference types will raise the exact
same questions, which should probably get the exact same answers. IOW,
both the question of whether to support reference types, and the
question of how to support them, should apply to both Boost.Optional and
Boost.Variant uniformly. It's an open issue.

6. propose it for TR2

For the benefit of all of us...

Thanks again for a great library,
Yuval


#ifndef VariantGet_h
#define VariantGet_h

#include <Utils/Variant.h>
#include <boost/mpl/assert.hpp>
#include <boost/mpl/contains.hpp>

namespace boost {

template <typename T>
struct get_common_visitor : public static_visitor<T>
{
        T operator()(T t) { return t; }
};

template <typename T, typename Variant>
typename add_reference<T>::type get_common(Variant &v)
{
        get_common_visitor<typename add_reference<T>::type> visitor;
        return v.apply_visitor(visitor);
}

template <typename T, typename Variant>
typename add_reference<const T>::type get_common(const Variant &v)
{
        get_common_visitor<typename add_reference<const T>::type> visitor;
        return v.apply_visitor(visitor);
}

template <typename T, typename Variant>
void assert_variant_has_type(const Variant &)
{
        BOOST_MPL_ASSERT((mpl::contains<typename Variant::types, T>));
}

template <typename T>
struct get_exact_visitor : public static_visitor<T>
{
        T operator()(T t) { return t; }
};

template <typename U, BOOST_VARIANT_ENUM_PARAMS(typename T) >
inline typename add_pointer<U>::type get_exact(
        boost::variant< BOOST_VARIANT_ENUM_PARAMS(T) >* operand
        BOOST_VARIANT_AUX_GET_EXPLICIT_TEMPLATE_TYPE(U))
{
        assert_variant_has_type<U>(*operand);
        typedef typename add_pointer<U>::type U_ptr;
        if (!operand) return static_cast<U_ptr>(0);
        if (operand->type() != typeid(U)) return static_cast<U_ptr>(0);

        detail::variant::get_visitor<U> v;
        return operand->apply_visitor(v);
}

template <typename U, BOOST_VARIANT_ENUM_PARAMS(typename T) >
inline typename add_pointer<const U>::type get_exact(
        const boost::variant< BOOST_VARIANT_ENUM_PARAMS(T) >* operand
        BOOST_VARIANT_AUX_GET_EXPLICIT_TEMPLATE_TYPE(U))
{
        assert_variant_has_type<U>(*operand);
        typedef typename add_pointer<const U>::type U_ptr;
        if (!operand) return static_cast<U_ptr>(0);
        if (operand->type() != typeid(U)) return static_cast<U_ptr>(0);

        detail::variant::get_visitor<const U> v;
        return operand->apply_visitor(v);
}

template <typename U, BOOST_VARIANT_ENUM_PARAMS(typename T) >
inline typename add_reference<U>::type get_exact(
        boost::variant< BOOST_VARIANT_ENUM_PARAMS(T) >& operand
        BOOST_VARIANT_AUX_GET_EXPLICIT_TEMPLATE_TYPE(U))
{
        typedef typename add_pointer<U>::type U_ptr;
        U_ptr result = get_exact<U>(&operand);

        if (!result)
                throw bad_get();
        return *result;
}

template <typename U, BOOST_VARIANT_ENUM_PARAMS(typename T) >
inline typename add_reference<const U>::type get_exact(
        const boost::variant< BOOST_VARIANT_ENUM_PARAMS(T) >& operand
        BOOST_VARIANT_AUX_GET_EXPLICIT_TEMPLATE_TYPE(U))
{
        typedef typename add_pointer<const U>::type U_ptr;
        U_ptr result = get_exact<const U>(&operand);

        if (!result)
                throw bad_get();
        return *result;
}

} // namespace

#endif /* ! defined VariantGet_h */


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk