mardi 25 juillet 2017

How do you abort the walk of a visitor in a sub-method?

I am working with a codebase that implements a custom datastructure consisting of several different node types, all of which provide an accept method that allows using visitor types for the implementation of operations such as printing, evaluating, reformulation, etc.

One of these operations is copying a node (and if they happen to be nonterminal any children) implemented in a templated copyCreator visitor. An excerpt of it is given below:

template<typename copyNodeType> struct copyCreator : public baseVisitor {
copyCreator(scope * globalScope) : baseVisitor(globalScope) {}
copyCreator(
            scope * globalScope,
            node * firstVisit) :
        baseVisitor(globalScope) {
    firstVisit->accept(*this);
}
~copyCreator() {
    copy.reset();
    for(auto ptr : openList) {
        delete ptr;
    }
}

std::unique_ptr<copyNodeType> copy = 0;
vector<nonterminalNode *> openList;
bool error = true;

// push to tree
template<typename nodeType>
void push(nodeType * ptr) {
    if (copy) {
        openList.back()->add_child(ptr); // if root is set, append to tree
    }
    else {
        //setCopy<nodeType>(ptr);
        auto temp = dynamic_cast<copyNodeType *>(ptr);
        if(temp) {
            copy = std::unique_ptr<copyNodeType>(temp);
            error = false;
        }
        else {
            delete ptr;
        }
    }
}

//...

//Example nonterminal node
void visit(struct sinNode & nod) {
    auto next = new sinNode;
    push(next);
    openList.push_back(next);
    nod.child->accept(*this);
    openList.pop_back();
};

My issue is that it may happen that we enter the push method for the first time and fail to encuonter the correct node type (copyNodeType). If this happens in a nonterminal node type such as the sinNode above, this will cause a nullptr to be pushed to the open list and the walk continues with an incorrect state.

The obvious solution is enclosing everything following the push(next) call in an if(next) { \\... }, but that is both cumbersome and doesn't seem like a very elegant solution. As the push method is being called from within the visit methods, I see no easy way to carry the information from a failed push outside without some flag that would need to be implemented in all the visit methods for every single nonterminal node type.

Is there some construct that allows exiting the first visit or otherwise implement a cancellation of the walk that I am not aware of?

Aucun commentaire:

Enregistrer un commentaire