Baz
Baz

Reputation: 13135

boost::static_visitor with multiple arguments

typedef boost::variant<int, double> Type;
class Append: public boost::static_visitor<>
{
public:
    void operator()(int)
    {}

    void operator()(double)
    {}

};

Type type(1.2);
Visitor visitor;
boost::apply_visitor(visitor, type);

Is it possible to change the visitor such that it receives extra data as follows:

class Append: public boost::static_visitor<>
{
public:
    void operator()(int, const std::string&)
    {}

    void operator()(double, const std::string&)
    {}
};

This string value changes during the lifetime of the Append object. Passing the string in via the constructor is not an option in this case.

Upvotes: 13

Views: 6740

Answers (3)

Janek_Kozicki
Janek_Kozicki

Reputation: 826

This one solves your problem:

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

typedef boost::variant<int, double> Type;
typedef boost::variant<const std::string> Extra;
class Append: public boost::static_visitor<>
{
public:
    void operator()(const int& a1, const std::string& a2) const {
        std::cout << "arg 1 = "<< a1 << "\n";
        std::cout << "arg 2 = "<< a2 << "\n";
    }

    void operator()(const double& a1, const std::string& a2) const {
        std::cout << "arg 1 = "<< a1 << "\n";
        std::cout << "arg 2 = "<< a2 << "\n";
    }
};

int main()
{
    Type type(1.2);
    Extra str("extra argument");
    boost::apply_visitor(Append(), type, str);
}

Here is a working Demo. You can send extra arguments - as many as you want. The restriction is that they must be wrapped inside boost::variant. However the compiler optimizes away the variants with single type inside. If you want more than two arguments you have to #include <boost/variant/multivisitors.hpp>, see https://www.boost.org/doc/libs/1_70_0/doc/html/boost/apply_visitor.html

Upvotes: 1

Mike
Mike

Reputation: 3084

Another option is to bind the extra arguments. You visitor class could look like this:

class Append: public boost::static_visitor<>
{
public:
    void operator()(const std::string&, int)
    {}

    void operator()(const std::string&, double)
    {}
};

Call it like so:

std::string myString = "foo";
double value = 1.2;
auto visitor = std::bind( Append(), myString, std::placeholders::_1 );
boost::apply_visitor( visitor, value );

Upvotes: 5

Mankarse
Mankarse

Reputation: 40623

The "additional argument" that gets given to each call is the this pointer. Use it to pass whatever additional information that you need:

#include <boost/variant.hpp>
typedef boost::variant<int, double> Type;
class Append: public boost::static_visitor<>
{
public:
    void operator()(int)
    {}

    void operator()(double)
    {}
    std::string argument;
};

int main() {
    Type type(1.2);
    Append visitor;
    visitor.argument = "first value";
    boost::apply_visitor(visitor, type);
    visitor.argument = "new value";
    boost::apply_visitor(visitor, type);
}

Upvotes: 20

Related Questions