12
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.

manlio
  • 18,345
  • 14
  • 76
  • 126
Baz
  • 12,713
  • 38
  • 145
  • 268
  • Why it's not an option? You could create another Append object with different strings in case that changes. – kennytm Oct 18 '12 at 12:40
  • At which time will the string value change? I don't see why it cannot be done with a constructor neither. – Mene Oct 18 '12 at 12:42
  • @KennyTM The Append class takes a number of dependencies via its constructor. If I reconstruct the Append object within my class, then this class will also need to take all the dependencies used by Append via its constructor. I had hoped that my class would simply take an Append object via its constructor. – Baz Oct 18 '12 at 12:50
  • @Baz: You can take an `Append` object in the constructor, and then set particular values inside the `Append` object before each call, without even needing to know about any of the other dependencies. – Mankarse Oct 18 '12 at 12:59
  • @Mankarse Yes this is what I was thinking of doing but its seems a little like a hack since one might forget to set the values before applying the visitor. – Baz Oct 18 '12 at 13:06
  • 1
    @Baz: You could make a helper function that first sets the relevant part of the `Visitor`, and then calls `apply_visitor`. I can't think of a way to enforce it in the type system, but you could keep a `bool` around that tracks whether the helper function has been called, and asserts if it hasn't. – Mankarse Oct 18 '12 at 13:12

3 Answers3

20

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);
}
Mankarse
  • 39,818
  • 11
  • 97
  • 141
  • 2
    If necessary the member need not be an `std::string` but can be e.g. an `std::string*` that can thus be rebound. – Luc Danton Oct 18 '12 at 12:59
  • @LucDanton: Indeed -- there are many possible variations on this general idea, depending on the exact needs of the situation. – Mankarse Oct 18 '12 at 13:02
5

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 );
Mike
  • 3,084
  • 1
  • 25
  • 44
1

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

Janek_Kozicki
  • 736
  • 7
  • 9