网络科技

    今日:344| 主题:245949
收藏本版
互联网、科技极客的综合动态。

[其他] Large C++ Legacy Applications: Tools

[复制链接]
被妳弄來沒了 发表于 2016-10-5 22:39:09
125 15

立即注册CoLaBug.com会员,免费获得投稿人的专业资料,享用更多功能,玩转个人品牌!

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
In the last weeks I have written about the contents of my “Large C++ Legacy Applications” talk: I’ve written about how dealing with those applicationsis a team game, about the importance of  planning the refactoring and tests and modularization . This post concludes the series with a look at the tools at our disposal.  
   Tooling

  There are tools that we can use to refactor and clean up the mess left behind for us. The most obvious is the tooling built into our IDEs: Many modern IDEs provide assistance beyond mere syntax highlighting. We have warnings shown to us while writing the code, i.e. they provide some static analysis. This that can help us find dubious spots in our code, which in turn can prevent errors and improve readability.
   Builtin IDE tools

  There are very few IDEs that I know of that provide tools for simple refactoring steps, like extracting and inlining functions and variables. That kind of functionality is common in IDEs for other languages, like Eclipse, IntelliJ and Visual Studio for C#. The more complex syntax of C++ however seems to make it more difficult to provide the same functionality for C++ IDEs.
   One of the better known examples of IDEs with emerging refactoring support is CLion, which I also use in the“4C environment” forFix. The refactoring support definitely has its limits, but as far as I can see the development is on a good way.
   IDE plugins

  Some IDEs provide plugin functionality which allows third party vendors to add refactoring aides. The most prominent examples are probably Visual Assist X and Resharper for C++. I have not used either myself, but as far as I know those tools are of at least a similar quality as CLion when it comes to refactoring support.
   Static analyzers

   While compilers and also IDEs already emit a lot ofwarnings about code that does not look quite right, there is no substitute for a proper static analyzer. There are lots of subtle things that can go wrong in large code bases. Static analyzers are tools designed to find all kinds of small omissions and subtle bugs, so you should use one or two of them .
   Consider using a newer IDE and compiler

  Modern IDE Tooling is getting better and better, but it mostly is only available on the newer IDEs. Plugins may not work on older IDEs, and modern static analyzers might warn about code that can not be fixed if you have to cater to the needs of some ancient compiler.
  In addition to the tool support, newer compilers also support the new C++ standards. This can enable us to write some code less tedious, safer and more performant.
  But of course it’s not that simple.
   
Large C++ Legacy Applications: Tools-1 (assistance,contents,planning,disposal,beyond)

   Switching the compiler

  Switching to another compiler can be a large task on its own. That is especially true if we skip multiple versions, from 32 bit to 64 bit compilation and/or to a different compiler vendor.
   One of the many small issues we can have is the size of pointers and integral types. There is code written a decade or two ago that simply assumes that the size of a pointer is and will be always 32 bit or 4 byte. Other code does compile without warnings only if   long   and   int   have the same size.
  For example, try grepping a million line code base for the number 4 – it’s not the best thing to spend several days on. Neither is the process of finding that subtle bug where the chunk of memory you allocated for two pointers suddenly only is enough for a single pointer.
  Or try to see the problem in this code:
  [code]std::pair splitOnFirstComma(std::string const& input) {
  unsigned position = input.find(',');
  if (position == std::string::npos) {
    return std::make_pair(input, "");
  }
  std::string first = input.substr(0, position);
  std::string second = input.substr(position+1, std::string::npos);
  return std::make_pair(first, second);
}
[/code]     unsigned   is an unsigned   int   , which usually has 32 bit. Comparing it to the 64 bit   npos   then always fails, which introduces one of those nasty subtle bugs we all love so dearly.
  All these small details have to be taken into account, found and fixed when switching the compiler. This is usually a series of small, isolated refactorings. Unless you are using a proprietary framework that comes with your old compiler and IDE, that is not available for the newer compiler you want to switch to. Then switching the compiler can become a large project on its own.
   Refactoring without tool support

   What if we are stuck with our ancient compiler and don’t have support by fancy tools? Well, we still have one tool at our disposal: The compiler itself. Using very small steps in the right order allows us to leverage the syntax checks the compiler has to do.
  For example, if we want to find all uses of a function, simply rename its declaration and definition and compile. The compiler will complain about unknown function names on each use of that function. Of course this assumes that you have no other declaration with the same name.
   With C++11, we can add   final   to a virtual function in the base class to find all classes that override the function – the compiler has to complain about each and every one of them.
   Example: factor out a function

  Let me finish this post with a step by step example to get help from the compiler while factoring out a function. Consider this original code:
  [code]std::shared_ptr createTree(TreeDataconst& data) {
  autorootData = data.root();
  autonewNode = std::make_shared();
  newNode->configure(rootData);
  for (auto&& subTreeData : data.children()) {
    newNode->add(createTree(subTreeData));
  }
  return newNode;
}
[/code]   We want to factor out the lines 2-4 into their own function   createNode   . I’ll assume a C++11 conformant compiler, but similar things can be done with older compilers, too.
  The first step is to add an additional scope around the lines in question to see which entities get created in the new function and used outside it. These will be the return values:
  [code]std::shared_ptr createTree(TreeDataconst& data) {
  {
    autorootData = data.root();
    autonewNode = std::make_shared();
    newNode->configure(rootData);
  }
  for (auto&& subTreeData : data.children()) {
    newNode->add(createTree(subTreeData)); //ERROR: newNode was not declared...
  }
  return newNode;
}
[/code]   So, our function needs to return   newNode   . The next step is to make our code compile again by putting the new scope into alambda. We can already give the lambda the name of the new function:
  [code]std::shared_ptr createTree(TreeDataconst& data) {
  autocreateNode = [&]{
    autorootData = data.root();
    autonewNode = std::make_shared();
    newNode->configure(rootData);
    return newNode;
  };
  autonewNode = createNode();
  for (auto&& subTreeData : data.children()) {
    newNode->add(createTree(subTreeData));
  }
  return newNode;
}
[/code]  The capture by reference makes all variables defined before the lambda accessible inside it. Which those are is the next thing to find out, by simply removing the capture:
  [code]std::shared_ptr createTree(TreeDataconst& data) {
  autocreateNode = []{
    autorootData = data.root(); //ERROR: data is not captured
    autonewNode = std::make_shared();
    newNode->configure(rootData);
    return newNode;
  };
  autonewNode = createNode();
  for (auto&& subTreeData : data.children()) {
    newNode->add(createTree(subTreeData));
  }
  return newNode;
}
[/code]   So, we have to get   data   into our function. This can be done by making it a parameter and passing it explicitly to the call:
  [code]std::shared_ptr createTree(TreeDataconst& data) {
  autocreateNode = [](TreeDataconst& data){
    autorootData = data.root();
    autonewNode = std::make_shared();
    newNode->configure(rootData);
    return newNode;
  };
  autonewNode = createNode(data);
  for (auto&& subTreeData : data.children()) {
    newNode->add(createTree(subTreeData));
  }
  return newNode;
}
[/code]  Now we have no dependencies of the lambda to its outer scope and vice versa. That means we can extract it as a real function:
  [code]autocreateNode(TreeDataconst& data) {
  autorootData = data.root();
  autonewNode = std::make_shared();
  newNode->configure(rootData);
  return newNode;
}

std::shared_ptr createTree(TreeDataconst& data) {
  autonewNode = createNode(data);
  for (auto&& subTreeData : data.children()) {
    newNode->add(createTree(subTreeData));
  }
  return newNode;
}
[/code]   Depending on our needs we can now add some further polishing, e.g. specifying the return type of   createNode   and using   rootData   as its parameter instead of   data   . However the main task of extracting the function is done, simply by relying on the compiler to tell us what to do by triggering compiler errors the right way.
   Conclusion

  Tools that help us refactoring and analyzing our legacy code base are important to the necessary refactoring. It is however possible, albeit tedious, to refactor our code even without such tools. So there is no real excuse to leave our legacy code rot for another decade.
友荐云推荐




上一篇:5 Actionable Insights from the 2017 B2B Content Marketing Benchmarks, Budgets, a
下一篇:Shopify now lets stores sell directly in Facebook Messenger
酷辣虫提示酷辣虫禁止发表任何与中华人民共和国法律有抵触的内容!所有内容由用户发布,并不代表酷辣虫的观点,酷辣虫无法对用户发布内容真实性提供任何的保证,请自行验证并承担风险与后果。如您有版权、违规等问题,请通过"联系我们"或"违规举报"告知我们处理。

旋宛 发表于 2016-10-6 00:07:41
土豪,交个朋友
回复 支持 反对

使用道具 举报

474501044 发表于 2016-10-6 00:22:14
占位编辑
回复 支持 反对

使用道具 举报

黄建川 发表于 2016-10-6 00:22:16
介是神马?!!
回复 支持 反对

使用道具 举报

datoukao 发表于 2016-10-6 00:22:22
喜欢的摇,不喜欢的滚。
回复 支持 反对

使用道具 举报

安静dê想 发表于 2016-10-6 00:22:25
怎么破
回复 支持 反对

使用道具 举报

大海无边 发表于 2016-10-21 22:46:16
走过路过请支持下!
回复 支持 反对

使用道具 举报

ptyks 发表于 2016-11-7 23:38:25
送快递的,LZ开门
回复 支持 反对

使用道具 举报

jianhong886 发表于 2016-11-8 22:07:25
垃圾内容,路过为证。
回复 支持 反对

使用道具 举报

iceking329 发表于 2016-11-11 09:24:50
吾在之时,首页之贴吾必全挽。吾走之时,只有无尽膜拜本尊之声。
回复 支持 反对

使用道具 举报

*滑动验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

我要投稿

推荐阅读

扫码访问 @iTTTTT瑞翔 的微博
回页顶回复上一篇下一篇回列表手机版
手机版/CoLaBug.com ( 粤ICP备05003221号 | 文网文[2010]257号 )|网站地图 酷辣虫

© 2001-2016 Comsenz Inc. Design: Dean. DiscuzFans.

返回顶部 返回列表