Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

boost::variant and polymorphism

I want to get pointer to base class from boost variant, if I put orignally pointer to derived class. Is there some way to achive this . The following code does not work.

class A{ public: virtual ~A(){}}; class B : public A{};
typedef boost::variant<A*,B*> MyVar;
MyVar var = new B;
A* a = boost::get<A*> (var); // the following line throws exception

Maybe someone have idea how to write my own get function which will test if the requested type is base class of the stored type of in the variant,and then do the appropriate cast

like image 449
user152508 Avatar asked Sep 13 '14 08:09

user152508


People also ask

What is boost variant?

Boost. Variant, part of collection of the Boost C++ Libraries. It is a safe, generic, stack-based discriminated union container, offering a simple solution for manipulating an object from a heterogeneous set of types in a uniform manner.

What is boost :: Static_visitor?

boost::static_visitor is a template. The type of the return value of operator() must be specified as a template parameter. If the operator does not have a return value, a template parameter is not required, as seen in the example. The second parameter passed to boost::apply_visitor() is a boost::variant variable.

What is std :: variant in C++?

class variant; (since C++17) The class template std::variant represents a type-safe union. An instance of std::variant at any given time either holds a value of one of its alternative types, or in the case of error - no value (this state is hard to achieve, see valueless_by_exception).


1 Answers

You can write your own visitor with templated operator() like below:

LIVE DEMO

#include <iostream>
#include <boost/variant.hpp>
#include <type_traits>

struct A { virtual ~A() {} virtual void foo() {} };
struct B : A { virtual void foo() { std::cout << "B::foo()" << std::endl; } };

template <typename T>
struct visitor : boost::static_visitor<T>
{
private:
    using Base = typename std::remove_pointer<
                        typename std::remove_cv<
                            typename std::remove_reference<T>::type
                        >::type
                    >::type;

    template <typename U>
    T get(U& u, std::true_type) const
    {
        return u;
    }

    template <typename U>
    T get(U& u, std::false_type) const
    {
        throw boost::bad_get{};
    }

public:
    template <typename U>
    T operator()(U& u) const
    {
        using Derived = typename std::remove_pointer<
                            typename std::remove_cv<
                                typename std::remove_reference<U>::type
                            >::type
                        >::type;

        using tag = std::integral_constant<bool
                         , (std::is_base_of<Base, Derived>::value
                           || std::is_same<Base, Derived>::value)
                           && std::is_convertible<U, T>::value>;

        return get(u, tag{});
    }
};

template <typename T, typename... Args>
T my_get(boost::variant<Args...>& var)
{
    return boost::apply_visitor(visitor<T>{}, var);
}

int main()
{    
    boost::variant<A*,B*> var = new B;

    A* a = my_get<A*>(var); // works!
    a->foo();

    B* b = my_get<B*>(var); // works!
    b->foo();
}

Output:

B::foo()
B::foo()

Q & A section:

This solution is weird!

No, it is not. This is exactly what the visitor classes in Boost.Variant are for. Similar solution already exists in latest release of Boost.Variant, which is boost::polymorphic_get<T>. Sadly it was designed for other purposes and cannot be used here.

like image 135
Piotr Skotnicki Avatar answered Oct 11 '22 17:10

Piotr Skotnicki