如何转换if以切换

How can I convert if to switch?

本文关键字:if 转换 何转换      更新时间:2023-10-16

有没有办法将C++ifs转换为switches?嵌套或非嵌套ifs:

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坚持认为,"当然,它应该检测转换是否有效。"。这是规则末尾条件的要点,例如,"if no_side_effect…"。。。noimpact’,用于检查对表达式的求值是否会更改某些内容,以及操作是否会影响表达式的值。

实现这些语义检查是困难的,因为必须考虑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}TSETCOND{whatever}MARKENDMARKIIFF类型构造的文件将被严重损坏。

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

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

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