A volte, uso lo stack delle chiamate come una struttura dati. Uso le variabili locali come elementi nello stack e utilizzo il membro dati di una classe per memorizzare l'elemento superiore. Quando uso questo modello, mi sento come se stessi facendo qualcosa di un po 'cattivo!
Ecco un esempio del modello. Il modello garantisce che le istruzioni break
e continue
salgano al posto giusto anche in presenza di istruzioni% nidificatewhile
, for
e switch
.
class StatementVisitor final : public ast::Visitor {
public:
void visitFlow(ast::Statement *body, llvm::BasicBlock *brake, llvm::BasicBlock *continoo) {
// Store the old blocks in local variables and push the new blocks.
llvm::BasicBlock *oldBreak = std::exchange(breakBlock, brake);
llvm::BasicBlock *oldContinue = std::exchange(continueBlock, continoo);
// Traverse a statement that might be a block that might have some
// break or continue statements in it.
body->accept(*this);
// Pop the top blocks and restore the previous blocks
continueBlock = oldContinue;
breakBlock = oldBreak;
}
void visit(ast::For &four) override {
// ...
visitFlow(four.body.get(), doneBlock, incrBlock);
// ...
}
void visit(ast::Break &) override {
// ...
funcBdr.ir.CreateBr(breakBlock);
// ...
}
void visit(ast::Continue &) override {
// ...
funcBdr.ir.CreateBr(continueBlock);
// ...
}
private:
// the blocks that continue and break statements should jump to
// these are on the top of the stack
llvm::BasicBlock *continueBlock;
llvm::BasicBlock *breakBlock;
FuncBuilder funcBdr;
};
Potrei usare std::stack<llvm::BasicBlock *>
e alla fine ottengo la stessa quantità di codice. Questo schema probabilmente salva la memoria e alcune allocazioni di heap in std::stack
. È così radicato nella mia mente (dopo averlo usato in altri due posti) che penso di usarlo prima di rendermi conto che potrei usare un std::stack
.
Dovrei evitare di usare questo modello?