访客模式.void* 是完全抽象的接口可接受的返回类型吗? [英] Visitor pattern. Is void* an acceptable return type for a completely abstract interface?

查看:31
本文介绍了访客模式.void* 是完全抽象的接口可接受的返回类型吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have an AST, represented in the usual way (a tree of nodes of an abstract type). I have several uses cases for traversing this tree (an optimizer, which returns another AST; IR code generation, which returns a llvm::Value*; and a debug analyzer, which simply outputs to stdout and returns nothing).

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

A visitor feels like the right way to go here, but the differing return types through each use case of the visitor make it hard to see how to implement an interface for this. I considered this:

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

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

Because of the lack of return value, each Visitor implementation would need to build up internal state and provide a result() method with a suitable return type. That is complex, however, since each call to visit() needs some context around the expression being visited (i.e. the call standing alone, or is it being used as part of a binary expression?). It makes it tricky for things like binary expression code generation to collect return values from visiting the nodes of the operands (I could do it with an internal temporary state variable or some such, but it feels over-engineered) and hard to reason about (the state keeps changing).

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

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

I could make it even more complex to avoid the "state keeps changing" issue by passing in a new visitor for each node. It just feels like this problem warrants a more functional solution.

真的,我想写得更简单,更容易阅读:

Really, I'd like to write the above much more simply and easier to read:

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

当然,现在我已经更改了返回类型,因此它不适合我的访问者合同.远不是为访问者的每个可能实现定义完全独立的接口并使 AST 知道这些访问者类型,似乎唯一真正合乎逻辑的事情是使用 void*.这种 API 是否保证使用 void*?有没有更好的方法来做到这一点?

Of course now I've changed the return type, so it doesn't fit my Visitor contract. Far from defining completely separate interfaces for each possible implementation of a a Visitor and making the AST aware of those visitor types, it seems the only really logical thing to use is a void*. Does this sort of API warrant the use of void*? Is there a better way to do this?

推荐答案

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

If I get this right, I might have something useful. Referring to your sample at https://gist.github.com/d11wtq/9575063, you cannot have

class ASTNode {
public:
   template <class T>
   virtual T accept(Visitor<T> *visitor);
};

因为没有模板虚函数.但是,您可能有一个泛型类

because there are no template virtual functions. However, you may have a generic class

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

和一个集合

template <class D, class... T>
struct hosts : host <D, T>... { };

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

so that, when the set of possible return types is limited, you can say e.g.

class ASTNode : public hosts <ASTNode, T1, T2, T3> {
public:
   // ...
};

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

Now you have three different Visitor contracts of return types T1,T2,T3.

这篇关于访客模式.void* 是完全抽象的接口可接受的返回类型吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆