18 十二月 2023
客户青睐Aspose产品,这些产品允许操作流行格式的协议和文件。其中大部分最初是为.NET开发的。与此同时,用于文件格式的业务应用在不同环境中运行。本文将描述我们如何通过构建一个从C#到C++ 的代码翻译框架,成功地为C++ 设置了Aspose产品的发布。保持这些产品的.NET版本的功能在技术上具有挑战性。
我们自己开发了必要的基础设施,实现了语言之间的代码翻译和.NET库函数的仿真。通过这样做,我们解决了通常被视为学术问题的难题。这使我们能够开始每月发布.NET产品的C++ 版本,从相应的C#代码版本中获取代码。此外,覆盖原始C#代码的测试也会与之一起翻译,确保所得解决方案的功能性与C++中专门编写的测试一致。
C#到C++代码转换器的成功基于CodePorting团队在设置自动C#到Java代码转换时的成功经验。创建的框架将C#类转换为Java类,同时适当地替换系统库调用。
对于这个框架,我们考虑了不同的方法。从头开始开发纯Java版本需要太多资源。一种选择是将Java代码中的调用传递到.NET环境,但这将限制我们未来可以支持的编程平台集。当涉及到很少发生的调用和广泛使用的数据类型时,调用传递是方便的。然而,在处理大量对象和自定义数据类型时,这变得很繁琐。
相反,我们思考如何完全将现有代码翻译到新平台上。这是一个当时的热门问题,因为每月都必须进行代码迁移,而且对所有产品都要进行,以产生具有相似功能的发布的同步流。
解决方案分为两部分:
以下的论点证实了这一计划在技术上是可行的:
System.Net
和 System.Drawing
。我们不会详细介绍 C# 到 Java 翻译器的更多细节,这需要专门的文章。总之,由于创建的代码翻译器,将 C# 产品转换为 Java 已经成为公司的常规做法。这个翻译器已经从一个简单的基于规则的文本转换器发展成了一个与源代码的 AST 表示一起工作的复杂代码生成器。
C# 到 Java 翻译器的成功帮助我们进入了 Java 市场,因此我们提出了使用相同方案开始发布 C++ 版本的问题。
为了能够发布我们产品的 C++ 版本,我们需要创建一个框架,允许我们将 C# 代码翻译成 C++,编译、测试并发送给客户。这些代码是一组库,每个库都有几百万行代码。代码翻译器的库组件必须涵盖以下内容:
许多读者可能会问,为什么我们没有考虑使用现有的解决方案,比如 Mono 项目。有几个原因:
从理论上讲,我们可以使用我们的翻译器将现有解决方案转换为 C++。然而,这需要在一开始就拥有一个完全功能的翻译器,因为没有系统库,无法调试任何翻译后的代码。此外,优化问题对于翻译后的产品代码来说比原来更加重要,因为系统库调用往往成为瓶颈。
让我们回到代码转换器的需求上。由于无法将 .NET 类型映射到 STL 类型,我们决定使用自定义的 Library 类型作为替代。该库被开发为一组适配器,允许通过类似于 .NET 的 API(与 Java 中相同)使用第三方库的功能。
在使用现有 API 翻译库时,翻译后的代码的一个重要要求是它应该在任何客户应用程序内运行。因此,我们不能在翻译后的代码中使用垃圾回收,因为它会覆盖整个应用程序。相反,我们的内存管理模型必须对 C++ 开发人员清晰可见。我们选择使用智能指针作为折衷方案。我们将在另一篇文章中描述我们如何成功地改变了内存模型。
CodePorting 具有强大的测试覆盖文化,将 C# 代码编写的测试应用于 C++ 产品将大大简化故障排除。代码翻译器必须能够翻译这些测试。
最初,手动修复翻译后的 Java 代码加速了开发和产品发布。然而,从长远来看,这显著增加了为每个版本准备发布所需的费用,因为每次出现翻译错误时都必须进行修复。通过将生成的 Java 代码与为两个连续的 C# 代码修订版本生成的翻译器输出之间的差异作为补丁提供给结果代码,可以管理这一问题,而不是每次都从零开始转换。尽管如此,我们决定优先考虑 C++ 框架修复,而不是结果代码修复,从而只修复每个翻译错误一次。