草庐IT

C++ 重构 : conditional expansion and block elimination

coder 2024-02-11 原文

我正在重构大量代码(主要是 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
}

对 value 的调用返回 bool 或 int。因为我知道这些调用总是返回的值,所以我做了一些正则表达式替换以将调用扩展到它们的正常值:

// 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
}

请注意,虽然这些值是固定的,但它们不是在编译时设置的,而是从共享内存中读取的,因此编译器目前没有在幕后优化任何东西。

虽然生成的代码看起来有点傻,但这种正则表达式方法实现了很多我想要的,因为它应用简单并消除了对调用的依赖,同时不改变代码的行为,而且编译器也可能然后优化它,知道一个 block 永远不会被调用,或者检查将始终返回 true。它还使查看更改内容并采取清理更改的最后一步变得相当容易(尤其是在与版本控制进行比较时),因此上面的代码最终如下所示:

// do something

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

// more stuff again

问题是我需要进行数百(可能数千)次更改才能从第二个正确但愚蠢的阶段获得最终的清理代码。

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

我知道有类似的问题,但我找不到任何类似这样的要求,也想知道自从他们被问到后是否出现了任何工具或程序?

最佳答案

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

正则表达式不是正则表达式,因为正则表达式不能可靠地解析 C++ 代码。 你需要的是 program transformation system .这是一个真正解析源代码的工具,可以将一组代码到代码的重写规则应用于解析树,并可以从更改的树中重新生成源文本。

我知道 Clang 在这方面有一些能力;它可以解析C++并构建一棵树,但它没有源到源的转换能力。您可以通过编写 AST 到 AST 的转换来模拟该功能,但恕我直言,这会带来更多不便。我相信它可以重新生成 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";

您还需要规则来简化(“折叠”)涉及算术的常量表达式,以及处理 switch 现在为常量的表达式的规则。要查看整数常量折叠的 DMS 规则,请参阅 Algebra as a DMS domain .

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

这些规则本身不能做的是跨分配的值(在您的情况下为常量)传播。为此,您需要流程分析,以便您可以跟踪此类分配(“达到定义”)。显然,如果您没有这样的任务或很少,您可以手动修补这些任务。如果这样做,您将需要进行流量分析;唉,DMS 的 C++ 前端还不完善,但我们正在努力;我们有适当的控制流分析。 (DMS的C前端有全流解析)。

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

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

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

有关C++ 重构 : conditional expansion and block elimination的更多相关文章

  1. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  2. ruby-on-rails - 如何重构 "shared"方法? - 2

    我正在使用RubyonRails3.2.2,我想从我的模型/类中“提取”一些方法。也就是说,在不止一个类/模型中,我有一些方法(注意:方法与用户授权相关,并被命名为“CRUD方式”),这些方法实际上是相同的;所以我认为DRY方法是将这些方法放在“共享”模块或类似的东西中。实现该目标的常见且正确的方法是什么?例如,我应该将“共享”代码放在哪里(在哪些目录和文件中)?如何在我的类/模型中包含提到的方法?你有什么建议?注意:我正在寻找“RubyonRails制作东西的方式”。 最佳答案 一种流行的方法是使用ActiveSupport关注点

  3. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  4. ruby - 如何计算 Liquid 中的变量 +1 - 2

    我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我

  5. ruby-on-rails - 在 haml View 中重构条件 - 2

    除了可访问性标准不鼓励使用这一事实指向当前页面的链接,我应该怎么做重构以下View代码?#navigation%ul.tabbed-ifcurrent_page?(new_profile_path)%li{:class=>"current_page_item"}=link_tot("new_profile"),new_profile_path-else%li=link_tot("new_profile"),new_profile_path-ifcurrent_page?(profiles_path)%li{:class=>"current_page_item"}=link_tot("p

  6. ruby - 需要重构为新的 Ruby 1.9 哈希语法 - 2

    这个问题在这里已经有了答案:HashsyntaxinRuby[duplicate](1个回答)关闭5年前。我有一个Recipe,其中包含以下未通过lint测试的代码:service'apache'dosupports:status=>true,:restart=>true,:reload=>trueend失败并出现错误:UsethenewRuby1.9hashsyntax.supports:status=>true,:restart=>true,:reload=>true不确定新语法是什么样的...有人可以帮忙吗?

  7. arrays - Ruby 数组 += vs 推送 - 2

    我有一个数组数组,想将元素附加到子数组。+=做我想做的,但我想了解为什么push不做。我期望的行为(并与+=一起工作):b=Array.new(3,[])b[0]+=["apple"]b[1]+=["orange"]b[2]+=["frog"]b=>[["苹果"],["橙子"],["Frog"]]通过推送,我将推送的元素附加到每个子数组(为什么?):a=Array.new(3,[])a[0].push("apple")a[1].push("orange")a[2].push("frog")a=>[[“苹果”、“橙子”、“Frog”]、[“苹果”、“橙子”、“Frog”]、[“苹果”、“

  8. += 的 Ruby 方法 - 2

    有没有办法让Ruby能够做这样的事情?classPlane@moved=0@x=0defx+=(v)#thisiserror@x+=v@moved+=1enddefto_s"moved#{@moved}times,currentxis#{@x}"endendplane=Plane.newplane.x+=5plane.x+=10putsplane.to_s#moved2times,currentxis15 最佳答案 您不能在Ruby中覆盖复合赋值运算符。任务在内部处理。您应该覆盖+,而不是+=。plane.a+=b与plane.a=

  9. ruby - Sinatra + Heroku + Datamapper 使用 dm-sqlite-adapter 部署问题 - 2

    出于某种原因,heroku尝试要求dm-sqlite-adapter,即使它应该在这里使用Postgres。请注意,这发生在我打开任何URL时-而不是在gitpush本身期间。我构建了一个默认的Facebook应用程序。gem文件:source:gemcuttergem"foreman"gem"sinatra"gem"mogli"gem"json"gem"httparty"gem"thin"gem"data_mapper"gem"heroku"group:productiondogem"pg"gem"dm-postgres-adapter"endgroup:development,:t

  10. ruby - Ruby 中字符串运算符 + 和 << 的区别 - 2

    我是Ruby和这个网站的新手。下面两个函数是不同的,一个在函数外修改变量,一个不修改。defm1(x)x我想确保我理解正确-当调用m1时,对str的引用被复制并传递给将其视为x的函数。运算符当调用m2时,对str的引用被复制并传递给将其视为x的函数。运算符+创建一个新字符串,赋值x=x+"4"只是将x重定向到新字符串,而原始str变量保持不变。对吧?谢谢 最佳答案 String#+::str+other_str→new_strConcatenation—ReturnsanewStringcontainingother_strconc

随机推荐