C++动态转换派生类及解释器Visitor模式函数指针问题
Hey there, let's work through your interpreter project issues step by step—building a Python-like interpreter in C++ with the Visitor pattern is a great learning exercise, so let's untangle these common C++ type system and design pattern hurdles:
1. 判断AST类型的最佳方式 & dynamic_cast非多态问题
First up, the dynamic_cast error about non-polymorphic classes: this happens because your base AST class doesn't have any virtual functions. C++ requires classes to be polymorphic (have at least one virtual function) for dynamic_cast to work.
Fix the polymorphic issue first
Add a virtual destructor to AST (to avoid memory leaks too) or, even better, implement the standard Visitor pattern's accept method to eliminate manual type checks entirely:
// AST.h class NodeVisitor; // Forward declaration to break circular dependencies class AST { public: // Virtual destructor makes AST polymorphic virtual ~AST() = default; // Pure virtual accept method (core of Visitor pattern) virtual void accept(NodeVisitor* visitor) = 0; }; class BinOp : public AST { public: AST* left; AST* right; char op; void accept(NodeVisitor* visitor) override { visitor->visitBinOp(this); } }; class Number : public AST { public: int value; void accept(NodeVisitor* visitor) override { visitor->visitNumber(this); } };
typeid vs dynamic_cast: which to use?
- Prioritize the
acceptmethod: This is the entire point of the Visitor pattern—you avoid manual type checks entirely, and adding new AST nodes only requires adding a correspondingvisitmethod andacceptimplementation, keeping code scalable. - If you must manually check types:
dynamic_castis safer thantypeid.dynamic_castreturnsnullptrif the conversion fails, whiletypeidonly checks for exact type matches (it won't handle subclasses ofBinOporNumbergracefully).
2. Creating function pointers for visit methods & fixing circular header dependencies
First, fix the circular header include error
The C2504: 'NodeVisitor' : base class undefined error comes from mutual header includes between NodeVisitor and Interpreter. Fix this with forward declarations and separating headers/implementations:
- NodeVisitor.h: Only forward-declare classes you need, don't include their headers
// NodeVisitor.h class AST; class BinOp; class Number; class NodeVisitor { public: virtual ~NodeVisitor() = default; // Pure virtual visit methods for each AST node type virtual void visitBinOp(BinOp* node) = 0; virtual void visitNumber(Number* node) = 0; // Unified entry point (uses accept under the hood) void visit(AST* node) { node->accept(this); } }; // Forward-declare Interpreter to avoid including its header here class Interpreter;
- Interpreter.h: Include
NodeVisitor.h(since it inherits from NodeVisitor)
// Interpreter.h #include "NodeVisitor.h" #include "AST.h" class Interpreter : public NodeVisitor { public: // Declare override methods (implement in .cpp) void visitBinOp(BinOp* node) override; void visitNumber(Number* node) override; };
- Interpreter.cpp: Put implementation here, where you can include all necessary headers
// Interpreter.cpp #include "Interpreter.h" void Interpreter::visitBinOp(BinOp* node) { // Your binary operation logic here } void Interpreter::visitNumber(Number* node) { // Your number handling logic here }
Creating function pointers for visit methods
If you really need to use member function pointers (though the accept pattern is better), here's how to define and use them:
// Define member function pointer types using VisitBinOpFunc = void (Interpreter::*)(BinOp*); using VisitNumberFunc = void (Interpreter::*)(Number*); // Get pointers to the methods VisitBinOpFunc binOpHandler = &Interpreter::visitBinOp; VisitNumberFunc numberHandler = &Interpreter::visitNumber; // Use the pointer with an instance Interpreter myInterpreter; (myInterpreter.*binOpHandler)(someBinOpNodeInstance);
If you want to map AST types to handlers (e.g., for a custom dispatch system), use std::function and std::bind (or lambdas) for flexibility:
#include <unordered_map> #include <functional> #include <typeindex> // Define a handler map in your Visitor/Interpreter std::unordered_map<std::type_index, std::function<void(Interpreter*, AST*)>> handlerMap = { {std::type_index(typeid(BinOp)), [](Interpreter* ip, AST* node) { ip->visitBinOp(dynamic_cast<BinOp*>(node)); }}, {std::type_index(typeid(Number)), [](Interpreter* ip, AST* node) { ip->visitNumber(dynamic_cast<Number*>(node)); }} }; // Use the map to dispatch Interpreter myInterpreter; auto handler = handlerMap.find(std::type_index(typeid(*someASTNode))); if (handler != handlerMap.end()) { handler->second(&myInterpreter, someASTNode); }
Skip extern "C" and dlsym—you don't need them here
These tools are for dynamic library loading and C/C++ interop, which is totally unnecessary for your use case. All your types and methods are known at compile time, so using these would just add unnecessary complexity.
内容的提问来源于stack exchange,提问作者Dimitar Mihaylov

