如何将 if 转换为切换?[关闭]

How can I convert if to switch? [closed]

提问人:Luchian Grigore 提问时间:11/24/2012 最后编辑:KaraLuchian Grigore 更新时间:12/5/2013 访问量:216

问:

有没有办法将 C++ s 转换为 es?嵌套或非嵌套:ifswitchif

 if ( a == 1 ) { b = 1; } /*else*/
 if ( a == 2 ) { b = 7; } /*else*/
 if ( a == 3 ) { b = 3; }

当然,它应该检测转换是否有效。

这不是微优化,而是为了清楚起见。

C++ 重构

评论

12赞 Pubby 11/24/2012
文本编辑器可以;)
6赞 Thomas Matthews 11/24/2012
这是微优化吗?编译器应该为这些创建一个跳转表。
3赞 Nik Bougalis 11/24/2012
我不知道有任何这样的工具。最简单的方法可能是使用您选择的编辑器进行搜索/替换。
2赞 Luchian Grigore 11/24/2012
@ThomasMatthews这是为了代码清晰。
2赞 πάντα ῥεῖ 11/24/2012
@LuchianGrigore 恐怕这是一个非常特殊且狭窄的用例,适用于任何高级 IDE。怀疑有什么东西覆盖了这一点......

答:

5赞 Ira Baxter 11/24/2012 #1

通常,您应该能够使用可以处理 C++ 的程序转换工具来执行此操作。

具体来说,我们的 DMS 软件再造工具包可以使用源到源重写来做到这一点。我认为这些已经接近完成这项工作:

  default domain Cpp;
  rule fold_if_to_switch_initial(s: stmt_sequence, e: expression,
                                 k1: integer_literal, k2: integer_literal,
                                 a1: block, a2: block):
                       stmt_sequence->stmt_sequence
        = "\s
           if (\e==\k1) \a1 
           if (\e==\k2) \a2 "
        -> "\s
            switch (\e) {
                case \k1: \a1
                     break;
                case \k2: \a2
                     break;
            }" if no_side_effects(e) /\ no_impact_on(a1,e);

   rule fold_if_to_switch_expand(s: stmt_sequence, e: expression, c: casebody,
                                 k: integer_literal, a:action)
                       stmt_sequence->stmt_sequence
        = "\s
           switch (\e) { \c } 
           if (\e==\k) \a "
        -> "\s
            switch (\e) {
                \c
                case \k: \a
                     break;
            }" if no_side_effects(e) /\ no_impact_on(c,e);

必须处理 if 控制语句而不是块的情况,俗气地通过以下方式完成:

      rule blockize_if_statements:(e: expression, s: statement):
            statement->statement
      =  "if (\e) \s" ->  "if (\e) { \s } ";

DMS 通过使用完整的语言解析器工作(是的,它确实有一个带有预处理器的完整 C++ 解析器选项) 处理源代码和重写规则。它不会被空格或注释所迷惑,因为它在解析树上运行。“...”里面的文字是 C++ 文本 (这就是“默认域 Cpp”声明所说的)与模式变量 \x;“...”之外的文本是 DMS 的规则元语法,而不是 C++。模式匹配树并将模式变量绑定到子树;规则右侧使用模式变量绑定进行实例化。应用转换后, 它从修订后的树中重新生成(修订后的)源代码。

OP坚持认为,“当然,它应该检测转换是否有效。这是规则末尾条件的要点,例如,“如果no_side_effect......no_impact“,用于检查计算表达式是否不会更改某些内容,以及操作是否不会影响表达式的值。

实现这些语义检查很困难,因为必须考虑 C++;难以置信的复杂语义。执行此操作的工具必须至少与编译器一样多(例如,名称、类型、声明的属性、读取和写入),然后必须能够推理结果。DMS目前只能实现其中的一部分(我们现在正在对C++11进行完全控制和数据流分析)。由于语义复杂,我也不指望其他重构工具能很好地完成这部分工作;我认为这种情况不太可能改善,因为推理 C++ 需要付出努力。

在 OP 的案例中(这在使用 DMS 等工具时经常发生),可以断言实际上这些语义谓词不会被违反(然后你可以用“true”替换它们,从而避免实现它们)或者你只关心特殊情况(例如,操作是对表达式中 on 以外的变量的赋值), 此时,检查将变得简单得多,或者您可以通过缩小可接受的语法范围来模拟检查:

rule fold_if_to_switch_expand(s: stmt_sequence, v: identifier, c: casebody,
                                 k: integer_literal, v2: identifier, k2: integer_literal)
                       stmt_sequence->stmt_sequence
        = "\s
           switch (\v) { \c } 
           if (\v==\k) \v2=\k2; "
        -> "\s
            switch (\v) {
                \c
                case \k: \v2=\k2;
                     break;
            }" if no_side_effects(c);

说了这么多,如果 OP 只有几百个已知的地方需要这种改进,他可能会咬紧牙关并使用他的编辑器来更快地完成它。如果他不知道位置,和/或有更多的实例,DMS 会更快地完成工作,并且可能更少的错误。

(我们使用 DMS 对 IBM Enterprise COBOL 进行了本质上相同的重构, 没有深入的语义检查)。

理论上,还有其他程序转换工具具有正确的机制(模式匹配、重写)可以做到这一点。据我所知,他们都没有进行任何有效的尝试来处理 C++。

评论

0赞 David Schwartz 11/24/2012
如果在编译时标头中有一个,但现在没有怎么办?似乎这种转换仅在编译时有效,但 OP 要求在编译之前完成转换,并且很可能在干预源代码更改的情况下完成。#define
0赞 Ira Baxter 11/24/2012
@David Schwartz:根据你的论点,任何代理,无论是机械的还是人类的,都无法做出可信的源代码更改,因为其他代理可以更改它所依赖的某些代码段。真;那又怎样?尽管如此,软件工程专业仍然蓬勃发展,主要是通过避免产生这种连锁反应的变化。
0赞 David Schwartz 11/24/2012
当然,没有任何源代码更改是绝对值得信赖的。因此,我的论点导致这个结论并不是其中的缺陷。正如你所说,解决方案是避免产生这种连锁反应的变化。正如我所指出的,这种变化会产生这种连锁反应。这是该行业通过避免而蓬勃发展的那种。
0赞 Ira Baxter 11/24/2012
你认为“将(这种风格的)ifs序列更改为案例陈述”会产生这种连锁反应?我们一直在重构代码,假设这种重构的连锁反应为零,否则它们就不会是重构。我想我和 OP 在一起。
0赞 David Schwartz 11/24/2012
阅读我的答案。我展示了这种变化是如何产生这种效果的。当然,这是人类一直在做的那种改变,但它完全不适合自动改变。(除非你可以看出它没有语义变化,除了像 .但在 OP 的案例中,现有代码可以达到多个案例,但重构的代码不能,这完全不适合自动化。#define case
0赞 Yakk - Adam Nevraumont 11/24/2012 #2

我会使用文本编辑器。

步骤1: 将 MARK 放在代码块之前,并可能带有前导制表符 步骤2: 将 END 放在代码块的末尾,并可能带有前导制表符 步骤3: 在某种 vi 中加载您的文件。运行如下内容:

/^^T*MARK/
:.,/^^T*ENDMARK/ s/^\(^T*\)if/\1IIFF/g
:%s/IIFF *([^)]*) *{ *\([^}]*\)};? *$/TEST{\1}TSET {\2} break;/
:%s/TEST{\([^=}]*\)==\([^}]*\)}TSET/COND{\1}case \2:/g
?^^T*MARK?
/COND{/
:. s/COND{\([^}]*\)}/switch(\1) {^M^T/
:%s/COND{\([^}]*\)}/^T/g
:%s/^\(^T*\)ENDMARK/\1} \/\/switch/g
:%s/^^TMARK//g

其中 ^T 是制表符,^M 是返回字符(您可能无法直接键入,具体取决于您使用的 vi/vim 版本)

代码是从我的头顶上写下来的,没有经过测试。但我以前做过这样的事情。此外,包含 and and and 和 类型结构的文件将被严重破坏。TEST{whatever}TSETCOND{whatever}MARKENDMARKIIFF

这不会检查有效性,它只是将完全按照您的格式将内容转换为 switch 语句。

有限的有效性检查可以通过一些花哨的屁股(对于 vi)来完成,确保等式表达式的左侧对于所有行都是相同的。它也不处理其他问题,但这很容易纠正。

然后,在签入之前,使用良好的比较工具对修改后的代码进行可视化扫描。