草庐IT

c++ - 你能用 C++ 做一个计算 goto 吗?

coder 2024-02-12 原文

Fortran有一种计算效率高的方法,称为“计算转到”。该构造使用分支表中的索引来执行直接转到。如果我没记错的话语法是:

go to index (label1, label2, ...)

索引用于引用括号列表中的代码指针(标签)。

我有一个案例,其中计算的 goto 是比 switch 语句更好的解决方案,我想构建一个,但我不知道如何构建。

现在在 jibes 和 slings 到来之前,编译器可以优化计算的 goto,但我不能保证它会。


始终可以使用 switch 语句。在某些情况下,switch 语句可以优化为跳转表(计算 goto 的实现)。

但是,这只有在 case 值的范围几乎是密集覆盖时才有可能(从低值到高值的范围内几乎每个整数都有一个 case 语句)。如果不是这种情况,则实现可能是二叉树。编译器编写者可以选择是否在适当的时候优化跳转表。在二叉树将始终满足 switch 语句的语义的地方,有时跳转表就足够的地方让我问我是否可以在适当的时候保证跳转表。我无法控制编译器编写器。

作为一个简单的案例,我经常编写词法分析器(FSMs),我使用三种数据结构,一种将输入映射到可接受的字母表,一种执行节点转换,一种基于当前状态和输入值。 FSM 的实现是一个 Mealy machine , 不是 Moore machine ,因此操作是在弧(转移)上执行的,而不是在节点上执行的。

执行的操作通常很小,通常不超过一行源代码。我认识到可以使用函数,并且它们的使用消除了对跳转表的需要。但我相信我不能“指向”内联函数,因此函数是封闭形式的可调用过程。

在大多数情况下,这比带或不带跳转表优化的 switch 语句效率低。如果我可以使用跳转表,那么我就可以避免编译器编写者认为优化的问题,并且能够编写高效的代码。

关于下面提出的与 Fortran 计算的 goto 相关问题的一般情况:这不是对那个/那些评论的批评。但是定性问题,即使它们是真实的,也没有回答问题。

下面有一个使用 void* &&label; 的答案,对此我要感谢你。但是,唉,正如您指出的那样,这是非标准的 C/C++,将来可能不会出现。所以,最好不要这样做。

我希望我已经回答了“获得更好的编译器”的评论。我希望我至少已经解决了使用函数指针的问题。最后,这对我来说是一个好奇的时刻。我不认为我应该提供为什么我认为这个问题具有一定承载力的杀菌历史。但现在我知道了。无论何时,我的意思是无论何时,我都会写信给这个小组,我最好告诉你们我所有的小鸭子是什么,这样它们就可以被正确地击落。

最佳答案

如果你用最近的 GCC 编译编译器(例如,GCC 7 或 GCC 6)——甚至对于 C 代码,旧版本的 GCC,您可以使用它的 labels as values语言扩展(因此C++11C++14 标准之外),它适用于C 和C++。前缀 && 运算符给出标签的地址,如果后跟 * 间接运算符则计算 goto .你最好让目标标签开始一些 block 。

例如:

#include <map>

int foo (std::map<int,int>& m, int d, int x) {
    static const void* array[] = {&&lab1, &&lab2, &&lab3 };
    goto *array[d%3];
lab1: {
        m[d]= 1;
        return 0;
    };
lab2: {
        m[2*d]=x;
        return 1;
    }
lab3: {
    m[d+1]= 4*x;
    return 2;
    }
}

(当然,对于上面的代码,普通的 switch 会更好,而且可能同样高效)

顺便说一句,最近Clang (例如, clang++-5.0)也接受该扩展。

(计算的 goto 对异常不友好,因此它们可能会在未来的 C++ GCC 版本中消失。)

还有threaded code编程技术,您可以使用它编写一些非常有效的(字节码)解释器,并且在那种特定情况下,代码保持非常可读(因为它是非常线性的)并且非常有效。顺便说一句,您可以使用宏和条件编译隐藏此类计算的 goto - 例如,#if-s-(例如,在不支持该扩展的编译器上使用 switch);那么您的代码将非常便携。有关 C 中的示例,请查看 OCamlruntime/interp.c .


对于 reference Eli Bendersky,计算的 goto 版本更快,原因有二:

  1. 由于边界检查,switch 每次迭代会多做一些事情。
  2. 硬件分支预测的影响。

编译器可以实现开关的多种变体。

  1. 使用 if 结构对开关空间进行二进制搜索。
  2. “案例”位置表(计算出类似的 goto)。
  3. 一个计算分支,要求所有情况都具有相同的代码大小,形成“代码数组”。

对于 OPs 状态机调度,第 2 项是最好的情况。它是唯一不需要返回到主 switch 调度位置的构造。因此,break; 可以将控制转移到下一个case。这就是该机制对分支预测更有效的原因。

关于c++ - 你能用 C++ 做一个计算 goto 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45380073/

有关c++ - 你能用 C++ 做一个计算 goto 吗?的更多相关文章

  1. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  2. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

    我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

  3. ruby-on-rails - 渲染另一个 Controller 的 View - 2

    我想要做的是有2个不同的Controller,client和test_client。客户端Controller已经构建,我想创建一个test_clientController,我可以使用它来玩弄客户端的UI并根据需要进行调整。我主要是想绕过我在客户端中内置的验证及其对加载数据的管理Controller的依赖。所以我希望test_clientController加载示例数据集,然后呈现客户端Controller的索引View,以便我可以调整客户端UI。就是这样。我在test_clients索引方法中试过这个:classTestClientdefindexrender:template=>

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

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

  5. ruby-on-rails - 使用一系列等级计算字母等级 - 2

    这里是Ruby新手。完成一些练习后碰壁了。练习:计算一系列成绩的字母等级创建一个方法get_grade来接受测试分数数组。数组中的每个分数应介于0和100之间,其中100是最大分数。计算平均分并将字母等级作为字符串返回,即“A”、“B”、“C”、“D”、“E”或“F”。我一直返回错误:avg.rb:1:syntaxerror,unexpectedtLBRACK,expecting')'defget_grade([100,90,80])^avg.rb:1:syntaxerror,unexpected')',expecting$end这是我目前所拥有的。我想坚持使用下面的方法或.join,

  6. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

  7. ruby - 为什么 SecureRandom.uuid 创建一个唯一的字符串? - 2

    关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?

  8. ruby-on-rails - Rails - 从另一个模型中创建一个模型的实例 - 2

    我有一个正在构建的应用程序,我需要一个模型来创建另一个模型的实例。我希望每辆车都有4个轮胎。汽车模型classCar轮胎模型classTire但是,在make_tires内部有一个错误,如果我为Tire尝试它,则没有用于创建或新建的activerecord方法。当我检查轮胎时,它没有这些方法。我该如何补救?错误是这样的:未定义的方法'create'forActiveRecord::AttributeMethods::Serialization::Tire::Module我测试了两个环境:测试和开发,它们都因相同的错误而失败。 最佳答案

  9. ruby - 用 Ruby 编写一个简单的网络服务器 - 2

    我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b

  10. ruby - 一个 YAML 对象可以引用另一个吗? - 2

    我想让一个yaml对象引用另一个,如下所示:intro:"Hello,dearuser."registration:$introThanksforregistering!new_message:$introYouhaveanewmessage!上面的语法只是它如何工作的一个例子(这也是它在thiscpanmodule中的工作方式。)我正在使用标准的ruby​​yaml解析器。这可能吗? 最佳答案 一些yaml对象确实引用了其他对象:irb>require'yaml'#=>trueirb>str="hello"#=>"hello"ir

随机推荐