访客模式.void*是否是完全抽象接口的可接受返回类型?

d11*_*wtq 6 c++ theory

我有一个AST,以通常的方式表示(抽象类型的节点树).我有几个遍历这个树的用例(一个优化器,它返回另一个AST; IR代码生成,它返回一个llvm::Value*;和一个调试分析器,它只是输出到stdout并且什么都不返回).

访问者感觉这是正确的方式,但访问者的每个用例的不同返回类型使得很难看到如何为此实现接口.我考虑过这个:

class Visitor;

class ASTNode {
public:
  virtual void accept(Visitor *visitor);
};

class Visitor {
public:
  virtual void visit(CallNode *node) = 0;
  virtual void visit(BinExprNode *node) = 0;
  // etc
};
Run Code Online (Sandbox Code Playgroud)

由于缺少返回值,每个Visitor实现都需要构建内部状态并提供result()具有合适返回类型的方法.然而,这很复杂,因为每次调用都visit()需要围绕被访问的表达式的某些上下文(即单独调用,或者它是否被用作二进制表达式的一部分?).对于像二进制表达式代码生成这样的事情来说,通过访问操作数的节点来收集返回值(我可以使用内部临时状态变量或其他类似的东西,但它感觉过度设计)并且难以推理(国家不断变化).

class OptimizingVisitor : public Visitor {
  ASTNode *m_result;
public:
  ASTNode *result() {
    return m_result;
  }

  void setResult(ASTNode *node) {
    m_result = node;
  }

  void visit(CallNode *node) {
    node->left()->accept(this);
    ASTNode *optimizedL = result();
    node->right()->accept(this);
    ASTNode *optimizedR = result();
    setResult(new CallNode(optimizedL, optimizedR));
  }

  // ... snip ...
};
Run Code Online (Sandbox Code Playgroud)

我可以通过传入每个节点的新访问者来避​​免"状态不断变化"问题变得更加复杂.只是感觉这个问题需要一个更实用的解决方案.

真的,我想写上面的内容更简单,更容易阅读:

class OptimizingVisitor : public Visitor {
public:
  ASTNode* visit(CallNode *node) {
    ASTNode *optimizedL = node->left()->accept(this);
    ASTNode *optimizedR = node->right()->accept(this);
    return new CallNode(optimizedL, optimizedR);
  }

  // ... snip ...
};
Run Code Online (Sandbox Code Playgroud)

当然现在我已经改变了返回类型,因此它不适合我的访问者合同.除了为每个可能的访问者实现定义完全独立的接口并使AST知道这些访问者类型之外,似乎唯一合乎逻辑的用法是a void*.这种API是否值得使用void*?有一个更好的方法吗?

iav*_*avr 4

如果我做对了,我可能会得到一些有用的东西。参考https://gist.github.com/d11wtq/9575063上的示例,您不能

class ASTNode {
public:
   template <class T>
   virtual T accept(Visitor<T> *visitor);
};
Run Code Online (Sandbox Code Playgroud)

因为没有模板虚函数。但是,您可能有一个通用类

template <class D, class T>
struct host {
   virtual T accept(Visitor<T> * visitor);
   // I guess, { return visitor->visit(static_cast <D*>(this)); }
};
Run Code Online (Sandbox Code Playgroud)

和一个集合

template <class D, class... T>
struct hosts : host <D, T>... { };
Run Code Online (Sandbox Code Playgroud)

因此,当可能的返回类型集有限时,您可以说例如

class ASTNode : public hosts <ASTNode, T1, T2, T3> {
public:
   // ...
};
Run Code Online (Sandbox Code Playgroud)

现在您拥有三种不同的返回类型访客合同T1,T2,T3