Search code examples
c++c++11design-patternslicensingvisitor-pattern

Defining Visitors Inline in Modern C++ for Hierarchical Graphs


This article (http://accu.org/index.php/articles/2021) introduces a very interesting concept of how to use inline visitors. I liked this approach and gave it a try. However, I encountered some issues.

  • There is no license given anywhere on the website. Is this sourcecode free to use?
  • The example in the article compiles only with clang 3.8.0 (not with gcc 5.3.1) and only with some small modifications (changing "auto v = begin_visitor..." to "auto v = begin_visitor()...").
  • Assume my code contains a non-flat graph (e.g. an AST). Some of those nodes in the graph hold references to subnodes. If my visitor traverse such a node, I would like to specify the order of how the subnodes have to be traversed. Is this even possible with this implementation?

Unfortunately the implementation is complicated to understand in depth.

Example:

// define the graph
struct Node {...};
struct Expr : Node {...};
struct Stat : Node {
  Expr& sub_node; // introduce a subnode
  // ...
}

// now the traversal with the inline visitor
auto v = begin_visitor<NodeVisitor>
.on<Expr>([&](Expr& e) 
{
  // do something with e
}) 
.on<Stat>([&](Stat& s) 
{ 
  // do something with s 
  // and then visit the sub_node of type 'Expr':
  s.sub_node.accept(*this); // "usual visitor" way: obviously wrong
  s.sub_node.accept(v); // cannot use variable in its initialization...
  ???
}) 
.end_visitor();
 p.accept(v); 

I appreciate every comment or hint towards this technique.

Thanks & regards


Solution

  • There is no license given anywhere on the website. Is this sourcecode free to use?

    No. But you can ask the authors for a license, and you are certainly allowed to implement your own "inline visitor" using their implementation as inspiration.

    Some of those nodes in the graph hold references to subnodes. If my visitor traverse such a node, I would like to specify the order of how the subnodes have to be traversed.

    The code as given does not do subnode traversal - it has no concept of submodes. You would have to do that in the relevant .on handler, same as you would for a normally implemented visitor. I imagine something like this (untested):

    sometype v;
    v = begin_visitor<Visitor>
        .on<MyTypeWithSubnodes>([&v](MyTypeWithSubnodes& x) {
            for (auto& subnode : x.subnodes) {
                subnode.accept(v);
        }
    );
    

    That being said, I think that the entire concept of "inline visitors" is overly complicated and misguided. It makes a simple concept complicated, will achieve lower performance than a normal visitor (because the compiler has a harder time optimizing), will increase compilation times, and will make error messages much more difficult to read.

    You can achieve the same effect in a much cleaner way using an inner class if you really want to contain everything inside your function:

    void my_func() {
      class MyVisitor : public Visitor {
        void visit(Triangle& t) { /* ... */ }
        void visit(Square& s) { /* ... */ }
      };
      MyVisitor vis;
      /* ... do things with vis ... */
    }
    

    The only thing that you do not get is direct access to the local variables, but I don't think that's worth sacrificing readability for.