[variant] Common base type of variant sub-types

Unions of POD types have a special case of allowing to access union value members through any union value which has those members. This is exemplified by following code: ---------- struct base { int shared; }; struct derived_1 : base { int shared_as_well; int not_shared_1; }; struct derived_2 : base { int shared_as_well; int not_shared_2; }; union union_type { derived_1 value_1; derived_2 value_2; } void f() { union_type v; v.value_1 = derived_1(); v.value_2.shared = 1; // This is OK. v.value_2.shared_as_well = 1; // This is OK as well. //v.value_2.not_shared_2 = 1; // This is not OK. } ---------- Such feature is quite useful. Sadly it is not supported by boost::variant. However a similar feature might be supported if boost::get was changed a bit. Lets consider another example: ---------- #include <boost/variant.hpp> struct base { int shared; }; struct derived_1 : base { int almost_shared; // If union was used this would be shared as well. int not_shared_1; }; struct derived_2 : base { int almost_shared; // If union was used this would be shared as well. int not_shared_2; }; typedef boost::variant<derived_1, derived_2> variant_type; void f() { variant_type v; v = derived_1(); assert(boost::get<base>(&v)); // This will fail. v = derived_2(); assert(boost::get<base>(&v)); // This will fail. } ---------- As can be seen boost::get fails if we specify a base type of a type which object is stored in variant. However if it would not fail but succeed we would have a feature similar to that special case with unions. And I know this can be done with visitor (just like boost::get is implemented) however I think it would be useful to wrap this functionality in a function. I think it is unlikely that such feature would break existing code. However it possibly might. To avoid breaking existing code a function similar to get could be introduced. I think I might implement this but I would prefer someone else to do this. :) Adam Badura

typedef boost::variant<derived_1, derived_2> variant_type;
void f() { variant_type v;
v = derived_1(); assert(boost::get<base>(&v)); // This will fail.
You can get the desired behaviour by applying a visitor.
I know that. I even admitted that in the post (at the end). But IMHO it does not change anything. boost::get can be implemented as a visitor (and in fact is implemented so) but still is provided as a function for convenience. Obviously it would not be wise to provide a function wrapper for each and every visitor. However the extended "get" I proposed is unique in its ways. Firstly it extends function already present in Boost. Secondly it provides for variant a feature available in union. Adam Badura

Adam Badura wrote:
Obviously it would not be wise to provide a function wrapper for each and every visitor. However the extended "get" I proposed is unique in its ways.
But it wouldn't be 'get'. There is a reason why it works like this. It's designed to test and extract for a specific type in one of the variant possibilities, without doing any implicit conversion. Adding the enhancement you suggest would change the meaning of 'get' and potentially break other code. So I think it would be best as a separate function. Here is a possible implementation: namespace boost { namespace detail { template<typename T> struct extract_visitor : static_visitor<T&> { T& operator()(T& t) const { return t; } T operator()(...) const { throw bad_visit(); } }; template<typename T> struct extract_pointer_visitor : static_visitor<T*> { T* operator()(T& t) const { return &t; } T* operator()(...) const { return 0; } }; } // namespace detail template<typename T, typename Variant> T& extract(Variant& v) { return apply_visitor(detail::extract_visitor<T>(), v); } template<typename T, typename Variant> const T& extract(const Variant& v) { return apply_visitor(detail::extract_visitor<const T>(), v); } template<typename T, typename Variant> T* extract(Variant* v) { return apply_visitor(detail::extract_pointer_visitor<T>(), *v); } template<typename T, typename Variant> const T* extract(const Variant* v) { return apply_visitor(detail::extract_pointer_visitor<const T>(), *v); } } // namespace boost
Secondly it provides for variant a feature available in union.
That could only work with unions if the types are binary compatible, which isn't necessarily the case.

Obviously it would not be wise to provide a function wrapper for each and every visitor. However the extended "get" I proposed is unique in its ways.
But it wouldn't be 'get'. There is a reason why it works like this. It's designed to test and extract for a specific type in one of the variant possibilities, without doing any implicit conversion.
Adding the enhancement you suggest would change the meaning of 'get' and potentially break other code. So I think it would be best as a separate function.
Firstly I know it might break existing code (although I don't think it is likely) and for the second time I dare to remind you that I did mention that in my original post. Secondly it is somewhat subjective what get is. For me it is just asking variant object for value of specified type and this can succeed with base type. But again this is subjective so lets leave it. I already mentioned in my original post that a new function could be used as well.
Here is a possible implementation:
I don't need it. In fact I already wrote one myself (although somewhat longer and more complex as it seems). I want it to be added to Boost.
Secondly it provides for variant a feature available in union.
That could only work with unions if the types are binary compatible, which isn't necessarily the case.
Yes. This is not exactly the same. But similar enough and a powerful feature (if it was not then unions would not have it). Adam Badura
participants (2)
-
Adam Badura
-
Mathias Gaunard