您如何将一个相当大(> 300K),相当成熟的C代码库转换为C++?
CI的类型被分为大致对应于模块的文件(即,比典型的基于OO类的分解更少的粒度),使用内部链接代替私有函数和数据,以及公共函数和数据的外部链接.全局变量广泛用于模块之间的通信.有一个非常广泛的集成测试套件,但没有单元(即模块)级别测试.
我想到了一个总体战略:
在C++的C子集中编译所有内容并使其正常工作.
将模块转换为大型类,以便所有交叉引用都由类名限定,但将所有函数和数据保留为静态成员,并使其工作.
使用适当的构造函数和初始化的交叉引用将巨大的类转换为实例; 根据需要使用间接访问替换静态成员访问; 让它工作.
现在,将项目作为一个错误的OO应用程序来处理,并编写单元测试,其中依赖项是易处理的,并分解为不属于它们的单独类; 这里的目标是在每次转型时从一个工作计划转移到另一个工作计划.
显然,这将是相当多的工作.关于这种翻译,是否有任何案例研究/战争故事?替代策略?其他有用的建议?
注1:程序是一个编译器,可能数以百万计的其他程序依赖于它的行为不会改变,所以批量重写几乎不是一个选择.
注2:来源近20年,每年可能有30%的代码流失(线路修改+增加/以前的总线路).换句话说,它被大量维护和扩展.因此,其中一个目标是增加可持续性.
[为了这个问题,假设转换为C++是强制性的,而将它留在C中则不是一种选择.添加这个条件的重点是清除"留在C中"的答案.]
几个月前刚刚开始几乎相同的事情(在一个已有十年历史的商业项目中,最初用"C++只不过是C与智能struct
s"的理念),我建议你使用相同的策略.用来吃大象:一次吃一口.:-)
尽可能将其分成几个阶段,这些阶段可以对其他部分产生最小的影响.正如Federico Ramponi所建议的那样,建立一个门面系统是一个良好的开端 - 一旦所有东西都有C++外观并通过它进行通信,你可以公平地确定它们不会影响它们之外的任何东西,从而改变模块的内部.
我们已经有了部分C++接口系统(由于以前较小的重构努力),所以这种方法在我们的案例中并不困难.一旦我们将所有内容都作为C++对象进行通信(花了几周时间,完成一个完全独立的源代码分支并将所有更改集成到主分支中,因为它们被批准),很少我们无法完全编译在我们离开这一天之前的工作版本.
转换尚未完成 - 我们已暂停两次临时发布(我们的目标是每隔几周发布一次点数),但它在路上很顺利,没有客户抱怨任何问题.我们的QA人员也发现了一个我记得的问题.:-)
关于什么:
在C++的C子集中编译所有内容并使其工作,并且
实现一组立面,使C代码保持不变?
为什么"必须翻译成C++"?您可以包装C代码,而无需将其转换为大型类等等.
您的应用程序有很多人在处理它,并且需要不被破坏.如果您认真考虑大规模转换为OO风格,那么您需要的是大规模转换工具来自动完成工作.
基本思想是将数据组指定为类,然后使用工具重构代码以将该数据移动到类中,将该数据上的函数移动到这些类中,并修改对该数据的所有访问以调用类.
您可以进行自动化预分析以形成统计群集以获得一些想法,但您仍然需要一位了解应用程序的工程师来决定应该对哪些数据元素进行分组.
能够执行此任务的工具是我们的DMS软件重新设计工具包.DMS具有强大的C解析器,用于读取代码,将C代码捕获为编译器抽象语法树,(与传统编译器不同)可以计算整个300K SLOC的流分析.DMS有一个C++前端,可以用作"后端"; 一个人编写将C语法映射到C++语法的转换.
大型航空电子系统上的主要C++再造任务给出了使用DMS进行此类活动的一些想法.请参阅www.semdesigns.com/Products/DMS/DMSToolkit.html上的技术论文,特别是通过自动程序转换重新设计C++组件模型
这个过程不适合胆小的人.但是,任何考虑手动重构大型应用程序的人都不会害怕辛苦工作.
是的,我与该公司有联系,是其首席架构师.
我会在C接口上编写C++类.不接触C代码将减少混乱的可能性并显着加快该过程.
一旦你有了C++界面; 那么复制+将代码粘贴到您的类中是一项微不足道的任务.正如您所提到的 - 在此步骤中进行单元测试至关重要.