Sirisha
Sirisha

Reputation: 11

Can we access the container from boost variant?

How do we know which container data is present in boost variant? My variant is having any datatype or container. how to access the type of the data from the container?

boost::variant<string, int> myvar = 245;
if(myvar.type() == typeid(int)){
    std::cout<<"integer data type"<< '\n';//I am able to get the type of integer/string
}

boost::variant<vector<int>, list<string>> v {1,2,3,4};//???

Upvotes: 1

Views: 211

Answers (2)

Ranoiaetep
Ranoiaetep

Reputation: 6637

There are mainly 2 ways for you to access the underlying element of a variant:

  • A compile-time type safe value visitation with apply_visitor
  • A run-time checked explicit value retrieval with get<T>

To use apply_visitor, you would first create a visitor functor by inherit from boost::static_visitor<T>, where T is the return type of the visitor:

struct my_visitor : boost::static_visitor<size_t>
{
    size_t operator()(std::vector<int> vec) const
    {
        for(int i : vec) std::cout << i << ',';
        return vec.size();
    }

    size_t operator()(std::list<std::string> list) const
    {
        for(std::string s : list) std::cout << s << ',';
        return list.size();
    }
};

Here both version of the visitor well print out the entire container, and return the size of the container. Notice the parameter type would correspond to the each types included with the variant. Then you can apply the visitor to the variant with:

size_t size = boost::apply_visitor(my_visitor(), v);

Also, since the two paths of the visitor behave almost the same, you can also just write a single template function:

struct my_visitor : boost::static_visitor<std::size_t>
{
    template<typename Container>
    std::size_t operator()(Container cont) const
    {
        for(auto s : cont) std::cout << s << ',';
        return cont.size();
    }
};

With C++14 or later, you may also use a lambda instead of a functor:

size_t size = apply_visitor(
    [](auto cont){ 
        for(auto s : cont) std::cout << s << ',';
        return cont.size();
    }, v
);

The other way is to rely on run-time check:

auto& vec = boost::get<std::vector<int>>(v);

Now vec would be a std::vector<int>&. If the contained type is not a std::vector<int>, this will throw a boost::bad_get. And if you used a type that's not included in the variant you provided, it will be a compile time error.

Upvotes: 2

Klaus
Klaus

Reputation: 25613

boost::variant<vector<int>, list<string>> v {1,2,3,4};//???

fails because the compiler can not determine the type from the given initializer list.

You simply can provide the type and all works as expected:

boost::variant<std::vector<int>, std::list<std::string>> v=std::vector<int> {1,2,3,4};

BTW: Why use boost::variant? You also have std::variant which fits maybe better?

Answering the questions in the comments: see full example below!

using vartype = boost::variant<std::vector<int>, std::list<std::string>>;
void access( vartype& v )
{
    // this is a very ugly idea, because on every change of the types of your variant, the numbers for the switch are unvalid!
    switch( v.which() )
    {    
        case 0: // first type of variant -> here it is vec<int>
            {
                auto& i = boost::get<std::vector<int>>( v ); 
                for ( auto& el: i ) { std::cout << el << std::endl; }
            }
            break;

        case 1: // second type in variant -> here it is list<string>
            {
                auto& s = boost::get<std::list<std::string>>(v);
                for ( auto& el: s ) { std::cout << el << std::endl; }
            }
            break;

        default:
            ;
    }    
}

// the visit method is much easier and better to maintain
struct MyVisitor: public boost::static_visitor<>
{
    void operator()(std::vector<int>& i) { for ( auto& el: i ) { std::cout << el << std::endl; } }
    void operator()(std::list<std::string>& s) { for ( auto& el: s ) { std::cout << el << std::endl; } }
};


int main()
{
    vartype vint = std::vector<int>{1,2,3,4};
    vartype vstring = std::list<std::string>{"This","is","easy"};

    access( vint );
    access( vstring );

    // or with visitor:
    MyVisitor vis;
    boost::apply_visitor( vis, vint );
    boost::apply_visitor( vis, vstring );

    // if you go with STL
    using stltype = std::variant< std::vector<int>, std::list<std::string>>;

    stltype si = std::vector<int>{ 5,4,3,2,1 };
    stltype ss = std::list<std::string>{"That","is","easier"};

    auto lambda = [](auto&& vec){ for ( auto& el: vec ) { std::cout << el << std::endl; }};

    std::visit( lambda, si );
    std::visit( lambda, ss );
}

Live

Upvotes: 0

Related Questions