0

I am using the visitor pattern to traverse a tree made up of many different types of nodes, something like this:

class Visitor
{
public:
    virtual void visit(TypeA &) = 0;
    virtual void visit(TypeB &) = 0;
    virtual void visit(TypeC &) = 0;
    virtual void visit(TypeD &) = 0;
    virtual void visit(TypeE &) = 0;
    virtual void visit(TypeF &) = 0;
    virtual void visit(TypeG &) = 0;
    virtual void visit(TypeH &) = 0;
    virtual void visit(Returner &) = 0;
    virtual void visit(Caller &) = 0;
};

Each of the visitor functions can potentially call any other visitor function since the Types are arranged in a tree. For example, visit(TypeA &) might be implemented as follows:

void ClassWhichInheritedVisitor::visit(TypeA & a){
    a.bChild->accept(*this);
    a.fChild->accept(*this);
    a.callerChild->accept(*this);
}

Now, I have a value generated in visit(Returner &) which I would like to pass back to the most recent call of visit(Caller &). My current solution is to throw the value from visit(Returner &) and catch it in visit(Caller &), however, I have read that exception handling can be very slow.

One solution might be to store the return value in some member variable and check for a value after each call to accept(*this) for any child, however, this makes the code bloated and difficult to read. Maybe this could be eased with a preprocessor macro?

#define visit(target);      \
    target->accept(*this);  \
    if (returnValuePresent) \
        return;

Is there a more idiomatic way to return this value without checking for a value after each call to accept(*this), considering that there might easily be 5/6 function calls between the call to visit(Caller &) and visit(Returner &)?

turbo_sheep
  • 109
  • 2
  • 7
  • Are you looking for a signal/slot mechanism? https://stackoverflow.com/questions/359928/which-c-signals-slots-library-should-i-choose – Jose Apr 21 '20 at 15:25
  • @Yipi No, when a value is generated by `visit(Returner &)` I would like to prevent any of the previous calls from continuing execution and pass control directly back to `visit(Caller &)`. This is different from a signal/slot which passes a value to a listener and invokes some behaviour. – turbo_sheep Apr 21 '20 at 15:41
  • A common idiom is to have `visit(...)` return a bool: `true` means continue as-is and `false` means stop the traversal immediately. That could be extended to include a result of some type if required. – G.M. Apr 21 '20 at 15:44
  • @G.M. I would like the `Visitor` to return `void` for a couple of reasons. Firstly from a performance standpoint, returning some value (a `std::string` in this case) will definitely increase the size of the stack frame and will likely call a copy for every return, even though only 2 functions are actually concerned with its return value. Besides, I mentioned that checking `if(!a.bChild->accept(*this)) return false;`, for every `accept` call is undesirable. I also have other visitor classes which don't need a return value and ideally I don't split them up into `VoidVisitor` and `StringVisitor`. – turbo_sheep Apr 21 '20 at 15:58

0 Answers0