You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++动态转换派生类及解释器Visitor模式函数指针问题

解答你的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 accept method: This is the entire point of the Visitor pattern—you avoid manual type checks entirely, and adding new AST nodes only requires adding a corresponding visit method and accept implementation, keeping code scalable.
  • If you must manually check types: dynamic_cast is safer than typeid. dynamic_cast returns nullptr if the conversion fails, while typeid only checks for exact type matches (it won't handle subclasses of BinOp or Number gracefully).

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:

  1. 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;
  1. 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;
};
  1. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 09:08:45