3

I have a problem with a variant value to an overloaded function. I want to call the overloaded function with an int or string depending on what is stored in the variant. This is how I want to do that, but i can't :

class X
{
    void foo(int i, int z) { /*use int i and z*/; }
    void foo(const std::string& s, int z) { /*use string s and z*/; }


    struct MyVisitor : public boost::static_visitor<int>  
    // !!! Here is the problem.
    // I can't return int or std::string,
    // so it's impossible to use template operator()
    {
        template<typename Data>
        const Data operator()(const Data data) const { return data; }
    };

public:

    /*somehow m_queue pushed ...*/

    void func_uses_variant(int z)
    {
        boost::variant<int, std::string> v = m_queue.pop();
        foo(boost::apply_visitor(MyVisitor(), v), z);
    }
private:
    SomeQueue m_queue;
}

Is it possible to write it using visitor or should I do something like this:

    void func_uses_variant(int z)
    {
        boost::variant<int, std::string> v = m_queue.pop();

        if (int* foo_arg = boost::get<int>(&v))
        {
            foo(*foo_arg, z);
        }
        else if (std::string* foo_arg = boost::get<std::string>(&v))
        {
            foo(*foo_arg, z);
        }
    }

I tried to to use variadics for MyVisitor but failed because of boost::static_visitor interface. Maybe there is a solution for that.

int z in function is just to show that there is not just boost::variant in the foo() parameters.

Kabik
  • 65
  • 9

2 Answers2

6

The whole point of visitors is to process each of the different types that could be held by the variant, in a form that doesn't involve get(), allows generics, and makes it easy to tell that you've handled all the options. If you can handle them all in one generic function, then you write a single template function in the visitor. If not, be more specific:

struct foo_visitor : public boost::static_visitor<std::string>  
{
    std::string operator()(int & i) const { return "this is int"; }
    std::string operator()(std::string &s) const { return "this is string"; }
};

This returns a common value from each possible type in the variant. Or you could use the visitor wrap the external function. Going back to the template function:

struct foo_visitor : public boost::static_visitor<void>  
{
    template<typename Data>
    void operator()(const Data &d) const { foo(d); }
};

EDIT[ It looks like this is provided by the library in the form of visitor_ptr, for single-argument functions. I think boost::visitor_ptr(foo) might be exactly equivalent. ]

For the extra argument, you can add a constructor to the visitor and store an extra data member that gets passed into the wrapped function:

struct foo_visitor : public boost::static_visitor<void>  
{
    int z;
    foo_visitor(int z_param) : z(z_param) {};

    template<typename Data>
    void operator()(const Data &d) const { foo(d, z); }
};

{
    boost::variant<int, std::string> v;
    v = 5;
    int z = 0;
    boost::apply_visitor(foo_visitor(z), v);
}

edit from @Kabik:

There is a way to do this using pattern matching with boost make_overloaded_function() and lambda.

link to experiment match function - http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0050r0.pdf

boost::variant<int, std::string> v;
v = 5;
int z = 0;
boost::apply_visitor(boost::bind<void>(boost::make_overloaded_function(
   [&](const int i) { foo(i, z); },
   [&](const std::string& str) { foo(str, z); }) _1
), v);
Peter
  • 14,559
  • 35
  • 55
  • I thought about that, but if foo() is invisible for visitor? i.e private method of some class and i can't call it from foo_visitor. no another way to get the value? – Kabik Aug 26 '16 at 17:14
  • Tell the whole story at once. Where does the "invisible" function live, and from what context do you need to apply the visitor? Please edit your question to include these details. – Peter Aug 26 '16 at 18:40
  • i don't know if u received message that i updated my question. but i updated it – Kabik Aug 27 '16 at 09:42
  • In your example foo is not invisible from either the caller or from the visitor. They're all in the same class. Are you getting an error? – Peter Aug 27 '16 at 14:19
3

there is a way to do this using pattern matching with boost make_overloaded_function and lambda.

link to experiment match function - http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0050r0.pdf

boost::variant<int, std::string> v;
v = 5;
int z = 0;
boost::apply_visitor(boost::bind<void>(boost::make_overloaded_function(
    [&](const int i) { foo(i, z); },
    [&](const std::string& str) { foo(str, z); }) _1
), v);
Kabik
  • 65
  • 9