提问人:Luchian Grigore 提问时间:11/24/2012 最后编辑:KaraLuchian Grigore 更新时间:12/5/2013 访问量:216
如何将 if 转换为切换?[关闭]
How can I convert if to switch? [closed]
问:
有没有办法将 C++ s 转换为 es?嵌套或非嵌套:if
switch
if
if ( a == 1 ) { b = 1; } /*else*/
if ( a == 2 ) { b = 7; } /*else*/
if ( a == 3 ) { b = 3; }
当然,它应该检测转换是否有效。
这不是微优化,而是为了清楚起见。
答:
通常,您应该能够使用可以处理 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++。
评论
#define
#define case
我会使用文本编辑器。
步骤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}TSET
COND{whatever}
MARK
ENDMARK
IIFF
这不会检查有效性,它只是将完全按照您的格式将内容转换为 switch 语句。
有限的有效性检查可以通过一些花哨的屁股(对于 vi)来完成,确保等式表达式的左侧对于所有行都是相同的。它也不处理其他问题,但这很容易纠正。
然后,在签入之前,使用良好的比较工具对修改后的代码进行可视化扫描。
评论