Visitor模式的替代方案:解决返回值使用不便的问题
搞定LLVM CodeGen中Visitor模式的返回值与实例化难题
我太懂你在LLVM教程里碰到的这个糟心事儿了——用虚函数实现codegen虽然上手快,但表达式节点和代码生成逻辑缠得死死的,想换Visitor模式解耦,结果一头栽进返回值处理的大坑,查了一堆资料全是复杂到头疼的方案,连直接创建CodeGen类传上下文都卡壳?别慌,咱们一步步来解决:
1. 给Visitor加带返回值的泛型接口,告别void返回的局限
别用那种传统的无返回值Visitor,直接整个带模板参数的版本,把返回类型明确下来。比如咱们LLVM codegen常用的返回类型是llvm::Value*,那Visitor可以这么写:
template<typename ReturnType> class ExprVisitor { public: virtual ReturnType visitBinaryExpr(BinaryExpr* expr) = 0; virtual ReturnType visitLiteralExpr(LiteralExpr* expr) = 0; // 按你的表达式类型加对应的visit方法 };
然后你的CodeGen类直接继承这个泛型Visitor,指定好返回类型:
class CodeGen : public ExprVisitor<llvm::Value*> { private: // 把需要的上下文直接存成成员变量 llvm::IRBuilder<>* builder; llvm::Module* module; std::map<std::string, llvm::Value*> symbolTable; // 比如符号表 public: // 构造函数直接传入所有需要的上下文 CodeGen(llvm::IRBuilder<>* b, llvm::Module* m) : builder(b), module(m) {} llvm::Value* visitBinaryExpr(BinaryExpr* expr) override { // 直接用成员变量里的builder,不用到处传参数 auto lhs = expr->getLHS()->accept(this); auto rhs = expr->getRHS()->accept(this); return builder->CreateAdd(lhs, rhs); // 示例加法生成逻辑 } llvm::Value* visitLiteralExpr(LiteralExpr* expr) override { return builder->CreateFPC(expr->getValue(), "literal"); // 字面量生成示例 } };
最后把表达式节点的accept方法改成返回对应类型:
class Expr { public: virtual llvm::Value* accept(ExprVisitor<llvm::Value*>* visitor) = 0; }; class BinaryExpr : public Expr { public: llvm::Value* accept(ExprVisitor<llvm::Value*>* visitor) override { return visitor->visitBinaryExpr(this); } };
这样一来,返回值的问题就彻底解决了,还保证了类型安全。
2. 简化CodeGen实例化:直接把上下文塞构造函数里
你说没法直接创建CodeGen类传内容?其实完全可以把IRBuilder、Module这些核心上下文在构造CodeGen的时候直接传进去,用的时候就像这样:
// 假设你已经初始化好了builder和module CodeGen codeGenInstance(builder, module); llvm::Value* generatedValue = someExpr->accept(&codeGenInstance);
如果后续需要加新的上下文(比如符号表、当前函数),直接在CodeGen里加成员变量,构造的时候一起传就行,完全不用搞什么复杂的全局变量或者单例。
3. 避开繁琐方案的小技巧
很多资料里的Visitor方案复杂,是因为想做通用到适配所有算法的万能模板,但咱们在LLVM codegen场景下根本不需要这么复杂:
- 不用搞太泛的模板,直接把返回类型固定成
llvm::Value*(或者用模板但指定死类型) - 把所有需要的上下文都封装在CodeGen实例里,不要通过visit方法传参数,减少冗余
- 如果碰到不需要返回值的节点(比如语句类),可以重载一个void版本的Visitor,或者用
std::optional<llvm::Value*>做返回类型,灵活处理
总的来说,Visitor模式在LLVM codegen里完全能用,核心就是贴合自己的需求定制带返回值的Visitor接口,并且把上下文数据直接封装在CodeGen类里,别照搬那些工业级的通用实现,简化之后用起来超顺手。
内容的提问来源于stack exchange,提问作者Thor Correia
相关产品推荐
相关产品推荐

