有没有办法将 C++ 转换if
为switch
es?嵌套或非嵌套if
s:
if ( a == 1 ) { b = 1; } /*else*/
if ( a == 2 ) { b = 7; } /*else*/
if ( a == 3 ) { b = 3; }
当然,它应该检测转换是否有效。
这不是微优化,而是为了清楚起见。
有没有办法将 C++ 转换if
为switch
es?嵌套或非嵌套if
s:
if ( a == 1 ) { b = 1; } /*else*/
if ( a == 2 ) { b = 7; } /*else*/
if ( a == 3 ) { b = 3; }
当然,它应该检测转换是否有效。
这不是微优化,而是为了清楚起见。
通常,您应该能够使用可以处理 C++的程序转换工具来做到这一点。
具体来说,我们的 DMS Software Reengineering Toolkit 可以使用源到源重写来做到这一点。我认为这些接近完成这项工作:
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”替换它们,从而避免实现它们)或者你只关心特殊情况(例如,操作是对表达式中除 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++。
我会使用文本编辑器。
第 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}TSET
andCOND{whatever}
和MARK
and ENDMARK
andIIFF
类型结构的文件将被严重破坏。
这不会检查有效性,它只是将您格式中的内容转换为 switch 语句。
有限的有效性检查可以用一些花哨的东西(对于 vi)来确保等式表达式的左侧对于所有行都是相同的。它也不处理其他问题,但这很容易纠正。
然后,在签入之前,您可以使用一个好的比较工具对修改后的代码进行可视化扫描。