我正在重构大量的代码(主要是C++),以删除大量已永久设置为给定值的临时配置检查。因此,例如,我将具有以下代码:

#include <value1.h>
#include <value2.h>
#include <value3.h>

...

if ( value1() )
{
    // do something
}

bool b = value2();

if ( b && anotherCondition )
{
    // do more stuff
}

if ( value3() < 10 )
{
    // more stuff again
}

值的调用返回 bool 或整数。由于我知道这些调用总是返回的值,因此我进行了一些正则表达式替换,以将调用扩展为它们的正常值:
// where:
//   value1() == true
//   value2() == false
//   value3() == 4

// TODO: Remove expanded config (value1)
if ( true )
{
    // do something
}

// TODO: Remove expanded config (value2)
bool b = false;

if ( b && anotherCondition )
{
    // do more stuff
}

// TODO: Remove expanded config (value3)
if ( 4 < 10 )
{
    // more stuff again
}

请注意,尽管这些值是固定的,但它们不是在编译时设置的,而是从共享内存中读取的,因此编译器当前并未在幕后进行任何优化。

尽管生成的代码看起来有些愚蠢,但是这种regex方法可以实现我想要的很多东西,因为它很容易应用,并且消除了对调用的依赖,同时又不改变代码的行为,并且编译器也可能随后优化了知道永远无法调用某个块或检查将始终返回true的很多事情。这也使得在查看变化时采取相当容易的方式(尤其是在与版本控制不同时),并采取了清理它的最后一步,因此上述代码中的代码最终如下所示:
// do something

// DONT do more stuff (b being false always prevented this)

// more stuff again

麻烦的是,我要从第二个正确但愚蠢的阶段进行数百(可能是数千个)更改,以获取最终的清理代码。

我想知道是否有人知道可以处理此问题的重构工具或我可以应用的任何技术。主要问题是C++语法很难完全扩展或消除,并且上述代码有很多置换。我觉得我几乎需要一个编译器来处理需要覆盖的语法变化。

我知道也有类似的问题,但是我找不到像这样的任何要求,并且还想知道自被问到以来是否出现了任何工具或程序?

最佳答案

听起来您有所谓的“僵尸代码” ...实际上已经死了,但就编译器而言仍然有效。对于大多数组织有组织的运行时配置变量的系统来说,这是一个很常见的问题:最终,一些配置变量达到永久的固定状态,但在运行时反复重新评估。

正如您已指出的,治愈方法不是regex,因为regex不能可靠地解析C++代码。
您需要的是program transformation system。这是一个真正解析源代码的工具,可以将一组代码到代码重写规则应用于解析树,并且可以从更改后的树中重新生成源文本。

我了解Clang在这里有一些功能;它可以解析C++并构建树,但是它不具有源到源的转换功能。您可以通过编写AST到AST的转换来模拟该功能,但是IMHO更加不便。我相信它可以重新生成C++代码,但我不知道它是否会保留注释或预处理程序指令。

我们的DMS Software Reengineering Toolkit及其C++(11) front end可以(并已用于)对C++源代码进行大量转换,并具有源到源的转换。 AFAIK,它是唯一可以做到这一点的生产工具。您需要的是一组转换,这些转换表示您对所关注的配置变量的最终状态的了解,以及一些简单的代码简化规则。以下DMS规则接近您可能想要的规则:

  rule fix_value1():expression->expression
    "value1()" -> "true";
  rule fix_value2():expression->expression
    "value2()" -> "false";
  rule fix_value3():expression->expression
    "value3()" -> "4";

  rule simplify_boolean_and_true(r:relation):condition->condition
     "r && true" -> "r".
  rule simplify_boolean_or_ture(r:relation):condition->condition
     "r || true" -> "true".
  rule simplify_boolean_and_false(r:relation):condition->condition
     "r && false" -> "false".
  ...
  rule simplify_boolean_not_true(r:relation):condition->condition
     "!true" -> "false".
  ...

  rule simplify_if_then_false(s:statement): statement->statement
      " if (false) \s" -> ";";
  rule simplify_if_then_true(s:statement): statement->statement
      " if (true) \s" -> "\s";
  rule simplify_if_then_else_false(s1:statement, s2:statement): statement->statement
      " if (false) \s1 else \s2" -> "\s2";
  rule simplify_if_then_else_true(s1:statement, s2: statement): statement->statement
      " if (true) \s1 else \s2" -> "\s2";

您还需要一些规则来简化(“折叠”)涉及算术的常量表达式,还需要一些规则来处理并在现在不变的表达式上切换。要查看整数常量折叠的DMS规则,请参阅Algebra as a DMS domain

与正则表达式不同,DMS重写规则不能“不匹配”代码。它们代表相应的AST,而AST是匹配的。因为是AST匹配,所以它们在空格,换行符或注释方面都没有问题。您可能会认为它们可能会对操作数的顺序产生麻烦(“如果遇到“false && x”,该怎么办?”);他们没有,因为 && 的语法规则||在DMS C++解析器中,被标记为关联和可交换的,并且匹配过程会自动将其考虑在内。

这些规则本身不能做的就是跨分配的值(在您的情况下为常数)传播。为此,您需要进行流程分析,以便您可以跟踪此类分配(“扩展定义”)。显然,如果您没有这样的分配或分配很少,则可以手动对其进行修补。如果这样做,则需要进行流量分析。 D,DMS的C++前沿还不存在,但我们正在努力。我们已经进行了控制流分析。 (DMS的C前端具有完整的流量分析)。

(2015年2月编辑:现在执行完整的C++ 14;函数/方法中的流分析)。

实际上,将这种技术应用于将近十年前来自IBM Tivoli的C和C++混合代码的1.5M SLOC应用程序取得了巨大的成功。我们不需要流量分析:-}

关于C++重构: conditional expansion and block elimination,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/10102610/

10-10 22:45