Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass `boost::static_visitor` instances to functions

I'm using boost::variant quite often in my projects. My colleagues now came up with the idea to pass around instances of specific boost::static_visitor<int> in order to customize the type of visitation. She had some code like the following:

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

typedef boost::variant<int, std::string> TVar;

struct Visitor1 : public boost::static_visitor<int> {
    template<typename T> 
    result_type operator()(const T&) {
        return 42;
    }
};

struct Visitor2 : public boost::static_visitor<int> {
    template<typename T>
    result_type operator()(const T&) {
        return 21;
    }
};

 int adder(const boost::static_visitor<int>& visitor, const TVar& visitable) {
     return visitable.apply_visitor(visitor) + 1;
 }

int main(int argc, char **args) {
    Visitor1 v1;
    Visitor2 v2;
    TVar x;

    std::cout << adder(v1, x) << std::endl;
    std::cout << adder(v2, x) << std::endl;
}

It looks perfectly sound to me, but it doesn't compile. My compiler says that expression will not yield a function that takes one 1 argument somewhere insider .

What are we doing wrong?

like image 738
Aleph0 Avatar asked Dec 23 '22 18:12

Aleph0


2 Answers

There's an overload for apply_visitor that takes only the visitor. This returns a partially applied function object that suits your needs, I guess:

  • http://www.boost.org/doc/libs/1_64_0/doc/html/variant/reference.html#header.boost.variant.apply_visitor_hpp

This overload returns a

Class template apply_visitor_delayed_t boost::apply_visitor_delayed_t — Adapts a visitor for use as a function object.

Personally, I like to add an overload to the visitor object like this:

struct MyVisitor {
    typedef void result_type;

    template <typename... Ts>
    result_type operator()(boost::variant<Ts...> const& v) const {
         return boost::apply_visitor(*this, v);
    }
    // optionally repeat for non-const `v`

    // normal variant handling overloads
};

That way, you can simply use the visitor object as the function oject.

like image 101
sehe Avatar answered Dec 29 '22 02:12

sehe


Firstly, you should mark your visitors' operator() overloads as const:

struct Visitor1 : public boost::static_visitor<int> {
    template<typename T> 
    result_type operator()(const T&) const {
        return 42;
    }
};

struct Visitor2 : public boost::static_visitor<int> {
    template<typename T>
    result_type operator()(const T&) const {
        return 21;
    }
};

Then, you need to change your adder to accept a template parameter instead of boost::static_visitor - it is your Visitor1 type that contains the operator() overload with the logic you desire. You need the "correct type" for the visitation to take place.

template <typename T>
int adder(const T& visitor, const TVar& visitable) {
    return visitable.apply_visitor(visitor) + 1;
}

live wandbox example

like image 20
Vittorio Romeo Avatar answered Dec 29 '22 03:12

Vittorio Romeo