如何转换if?

时间:2012-11-23 19:21:21

标签: c++ refactoring

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

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

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

这不是微观优化,只是为了清晰起见。

2 个答案:

答案 0 :(得分:5)

通常,您应该能够使用可以处理C ++的program transformation tool来执行此操作。

具体而言,我们的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坚持认为,“它当然应该检测转换是否有效。”这是规则结束时条件的要点,例如'if no_side_effect ... no_impact',用于检查评估表达式是否不会改变某些内容,以及操作不会影响值的表达。

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

在OP的情况下(当使用像DMS这样的工具时,这种情况经常发生),可以断言事实上这些语义谓词不会被违反(那么你可以用“true”替换它们,从而避免实现它们)或者您只关心特殊情况(例如,该操作是对表达式中除变量之外的变量的赋值),此时检查变得更加简单,或者您可以通过缩小可接受的语法来模拟检查:

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 ++。

答案 1 :(得分:0)

我会使用文字编辑器。

第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的版本)

代码写在我的头顶,没有经过测试。但我以前做过这样的事情。此外,包含TEST{whatever}TSETCOND{whatever}以及MARKENDMARK以及IIFF类型构造的文件也会受到严重损坏。

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

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

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