c++boosttree-traversalboost-variantstatic-visitor

Having a static_visitor modify a Boost recursive variant while traversing it?


I am using Boost's variant type extensively to build trees. More precisely, I parse a tree from a grammar using Boost's Qi, and then I traverse the tree to annotate each node with an integer - at least that is what I want to do.

I just realized that since the static_visitor does not visit the node as pointers, that I can't possibly modify the value field. So I tried to have static_visitor work on a pointers of the variant type, instead of the variant itself.

A simplified example:

typedef struct s_node node;


typedef boost::variant<
        int,
        boost::recursive_wrapper<node>,
        > tree;

struct s_node
{
  tree left, right;
  double value;

  explicit s_node(const expr& l, const expr& r) : oper1(l), oper2(r) { value = -1.0; }
};

struct Traversal : boost::static_visitor<void>
{
  void operator()(int *i) const { return; }

  void operator()(node *b) {
    b->value = 10.0;
  }
};

But it does not work. When I try to do:

Traversal t;
boost::apply_visitor(t, &tree);

I get an error:

test.cpp:253:21: error: no matching function for call to 'apply_visitor'
...

How can I make the static_visitor do what I want? Is there a better way to do this? At the moment, the only idea I am considering is to make the field inside the node structure a pointer to an int, instead of an int.


Solution

  • You can very much modify objects taken by reference:

        void operator()(int) const { }
    
        void operator()(s_node& b) const {
            b.value = 10.0;
        }
    

    See it Live On Coliru, output:

    Before: s_node {5, s_node {7, 42 /*value: -1*/} /*value: -1*/}
    After:  s_node {5, s_node {7, 42 /*value: -1*/} /*value: 10*/}
    

    Full sample:

    #include <boost/variant.hpp>
    #include <iostream>
    
    struct s_node;
    
    typedef boost::variant<
            int,
            boost::recursive_wrapper<s_node>
        > expr;
    
    struct s_node
    {
        expr oper1, oper2;
        double value;
    
        explicit s_node(const expr& l, const expr& r)
            : oper1(l), oper2(r), value(-1) { }
    
        friend std::ostream& operator<<(std::ostream& os, s_node const& n) {
            return os << "s_node {" << n.oper1 << ", " << n.oper2 << " /*value: " << n.value << "*/}";
        }
    };
    
    struct Traversal : boost::static_visitor<void>
    {
        void operator()(int) const { }
    
        void operator()(s_node& b) const {
            b.value = 10.0;
        }
    };
    
    int main()
    {
        expr x = s_node(5, s_node(7, 42));
    
        std::cout << "Before: " << x << "\n";
    
        boost::apply_visitor(Traversal(), x);
    
        std::cout << "After:  " << x << "\n";
    }