草庐IT

c++ - 访客模式。 void* 是完全抽象接口(interface)可接受的返回类型吗?

coder 2024-02-03 原文

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

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

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

由于缺少返回值,每个 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 ...
};

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

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

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

当然,现在我已经更改了返回类型,所以它不适合我的访客契约(Contract)。远非为访问者的每个可能实现定义完全独立的接口(interface)并使 AST 知道这些访问者类型,似乎唯一真正合乎逻辑的使用是 void*。这种 API 是否保证使用 void*?有更好的方法吗?

最佳答案

如果我做对了,我可能会有一些有用的东西。在 https://gist.github.com/d11wtq/9575063 引用您的 sample , 你不能有

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

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

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

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

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

现在您有三个不同的返回类型 T1、T2、T3 的访问者合约。

关于c++ - 访客模式。 void* 是完全抽象接口(interface)可接受的返回类型吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22430211/

有关c++ - 访客模式。 void* 是完全抽象接口(interface)可接受的返回类型吗?的更多相关文章

  1. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  3. ruby - 完全离线安装RVM - 2

    我打算为ruby​​脚本创建一个安装程序,但我希望能够确保机器安装了RVM。有没有一种方法可以完全离线安装RVM并且不引人注目(通过不引人注目,就像创建一个可以做所有事情的脚本而不是要求用户向他们的bash_profile或bashrc添加一些东西)我不是要脚本本身,只是一个关于如何走这条路的快速指针(如果可能的话)。我们还研究了这个很有帮助的问题:RVM-isthereawayforsimpleofflineinstall?但有点误导,因为答案只向我们展示了如何离线在RVM中安装ruby。我们需要能够离线安装RVM本身,并查看脚本https://raw.github.com/wayn

  4. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  5. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  6. ruby - 是否有用于序列化和反序列化各种格式的对象层次结构的模式? - 2

    给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最

  7. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  8. postman接口测试工具-基础使用教程 - 2

    1.postman介绍Postman一款非常流行的API调试工具。其实,开发人员用的更多。因为测试人员做接口测试会有更多选择,例如Jmeter、soapUI等。不过,对于开发过程中去调试接口,Postman确实足够的简单方便,而且功能强大。2.下载安装官网地址:https://www.postman.com/下载完成后双击安装吧,安装过程极其简单,无需任何操作3.使用教程这里以百度为例,工具使用简单,填写URL地址即可发送请求,在下方查看响应结果和响应状态码常用方法都有支持请求方法:getpostputdeleteGet、Post、Put与Delete的作用get:请求方法一般是用于数据查询,

  9. ruby - 如何计算 Liquid 中的变量 +1 - 2

    我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我

  10. ruby-on-rails - environment.rb 中设置的常量在开发模式中消失 - 2

    了解Rails缓存如何工作的人可以真正帮助我。这是嵌套在Rails::Initializer.runblock中的代码:config.after_initializedoSomeClass.const_set'SOME_CONST','SOME_VAL'end现在,如果我运行script/server并发出请求,一切都很好。然而,在我的Rails应用程序的第二个请求中,一切都因单元化常量错误而变得糟糕。在生产模式下,我可以成功发出第二个请求,这意味着常量仍然存在。我已通过将以上内容更改为以下内容来解决问题:config.after_initializedorequire'some_cl

随机推荐