同城58网 科技 不喜欢 D 和 C++,程序员将 58000 行代码移植到 Jai 语言?

不喜欢 D 和 C++,程序员将 58000 行代码移植到 Jai 语言?

要:将已有的上万行代码迁移至另一种编程语言,从来就不是一件容易决定的事情,而本文作者却信心满满地要将 5.8 万行代码全部用另一种不那么主流的语言重写,这是为什么呢?

接:https://www.yet-another-blog.com/porting_the_game_to_jai_part0/

者 | Simon van Bernem

译者 | 弯月 责编 | 郑丽媛

在这篇文章中,我将分享把一款正在开发的游戏移植到 Jai 语言的经过。游戏本身主要是用 D 和 C++ 编写的,总共有 58,620 行代码(不包括库)

原因

你可能想问,为什么要将如此大规模的程序移植到另一种编程语言?你完全可以等到新项目再用新语言嘛!

我之所以移植这些代码,原因主要有以下几个:

最重要的原因是:我喜欢 Jai

为什么放弃 C++

网上有很多文章诉说了 C++ 的缺点,所以我不打算在此赘述。简单来说,C++ 几十年的发展积累了很多错误决定,我们没有任何方法去摆脱它们。标准库是一场灾难,使用其他人的代码也非常困难,而且不知何故,C++ 每次添加的新功能都有陷阱。

虽然 C++ 的这些缺点也不至于 糟糕到让避之不及,但它确实给我带来了很多痛苦。此外,这些年来 C++ 的发展似乎不尽如人意,我不觉得这门语言会越来越好,所以我还是想逃离 C++ 生态系统。

为什么放弃 D 语言

2019 年,当我开始开发这款游戏时,已决定放弃 C++,但我不确定应该选用何种语言。最终,我选择了 D 语言,因为这门语言与 C++ 类似,但没有 C++ 的那些缺点。然而不幸的是,事实证明 D 语言也有一些 C++ 中不存在的缺点。

虽然 D 语言有一些优点,比如更强大的元编程、不需要头文件、没有未初始化的值等,但相较于缺点而言,这些优点不值一提。

我在 Windows 的两款 D 编译器(dmd 和 ldc2)之间来回折腾了 4 年之久,到头来却发现在 Windows 上编写 D 代码,只适合个人的业余项目或早期不成熟的软件,该语言完全不像有 20 多年的发展经历。

就目前的情况来看,我不建议任何人使用 D 语言在 Windows 开发正式项目。相较而言,继续使用 C++ 才是更好的选择。根据我多年的经验来看,在 Windows 编写 D 代码所面临的最大问题在于,其调试信息千疮百孔:

网上有人告诉我,一直以来 DMD 的调试信息就存在很多质量上的问题,但不幸的是,上述大部分问题不仅限于 DMD,Windows 的两个编译器都有这些问题。除了调试之外,元编程的核心部分还存在其他问题和缺点:

总之,虽然从某些方面来看,D 确实比 C++ 略胜一筹,但其他方面的小问题非常多,累积起来导致使用 D 语言编程也非常痛苦。糟糕的调试信息和垃圾收集的需求很致命,我的整体感受是,D 的创始人对 C++ 的看法似乎与我截然不同。我只是希望改进 C++,而不是用一些 C++ 的问题来换取 D 的其他问题。现在我对调试器有严重的信任问题。

为什么选择 Jai

Jai 是 Jonathan Blow 于 2014 年开发的一款编程语言,而编译器一直到 2019 年 12 月才开始内测。如今封闭测试仍在进行中,大约两个月前,我应邀参加了这项测试。

Jai 的设计初衷也是希望改进 C++,但与 D 语言不同,Jai 正在对 C++ 做出有意义的改进。在我看来,最重要的改进包括编译速度更快,以及允许通过无限制的编译时执行来实现元编程。

请注意,这里我所说的编译速度提升可不止 20%,而是 10~100 倍;而且你能在编译期间执行任何操作,不仅限于元编程。尤其是,元编程与编译时编译器 API 的结合使用具有深远的影响,例如你不再需要构建系统,或启用复杂的自定义检查。除此之外,Jai 还对 C++ 进行了其他方面的改进,比如更好的默认值、更简单的语法、更实用的标准库、命名函数参数、上下文、using 等等。我希望我的游戏从 D 移植到 Jai,能够获得以下提升:

我希望通过这篇文章记录我的期望,将来可以回过头来检查有多少期望真的实现了。

为什么不是其他语言

如上所述,我不喜欢 C++ 和 D 语言,而 Jai 看起来很不错。那么,其他编程语言呢?似乎Rust 也是一个很好的备选。这门语言风头正盛,而且还有一个热心的社区,但我个人认为 Rust 并没有做出正确的权衡。

它的支持者都是唯“内存安全”是论者,考虑到如此多的漏洞都是内存安全引发的,我可以理解这种心态,但他们忽略了其他高安全性、高质量的方法。例如,我相信如果 C 和 C++ 没有零终止字符串、默认初始化值,那么大部分漏洞都不会存在;再加上合适的指针+长度类型,就可以用边界检查代码取代 90% 的指针计算;然后再建立一种文化,不鼓励大家自己想办法自行管理内存。

除此之外,我认为,我们在寻找“安全的”代码时完全忽视了我们拥有如此多漏洞的最主要的因是,我们的文化对复杂性的容忍,甚至是鼓励。总之,忍受 Rust 慢吞吞的编译并接受借用检查器是一种极端的解决方案,并没有解决最重要的问题,这是一个文化上的问题。另一方面,Jai 非常在意复杂性,并努力建立正确的文化。

对于其他不太受欢迎的语言,例如 Zig,我只能说虽然它们可能具有巨大的潜力,但我并无法相信它们是正确的选择。我不是说这些语言不好,只是它们不适合我。

移植方法

在本文开头,我曾说过我认为此次移植能够顺利进行,原因是我的游戏中有两个系统,对此次移植有很大的帮助:

实际的功能和上面的描述有一些细微的差别,但不会影响整体的逻辑。根据这些特性,我的移植计划如下:

关于该方法是否有效,我需要考察两个关键问题:

第一个问题取决于状态哈希覆盖了多少代码。一部分代码需要判断游戏是否正在回放,这部分代码在回放时有不同的行为,因此无法完成真正的哈希处理。例如,写入文件的功能在回放时会直接丢弃所有数据,因此如果移植在写入文件的代码时引入 bug,则不会被哈希处理注意到。幸运的是,大多数代码不属于这一类。

最初,只有很小一部分代码使用了哈希处理,例如物理模拟,但最近我设法进行了扩展,在向动态数组插入数据时,用哈希来记录其大小和容量。这意味着,插入动态数组的代码中的 bug 很快就会被发现。由于动态数组的使用在我的代码中随处可见,所以对于庞大且复杂的数学算法来说,即便是一个很小的变化,也能带来立竿见影的效果。

第二个问题是一个经典的编程问题:代码的解耦性如何?这个问题非常有趣,因为在移植的过程中,我将亲眼目睹我的代码库中究竟封存了多少不为我所知的复杂性。一个明显的问题是模板函数,这些代码无法直接移植,因为函数的定义和调用必须在同一个编译器中,模板才能发挥作用,除非你手动实例化模板。我的代码库中有大量的模板化代码,但大多不依赖于容器或序列化,所以我希望不会引起太大的麻烦。

移植过程

下面这张图是移植前的代码库状态:

整个代码库有 45,701 行 D 代码和 12,919 行 C++ 代码,总共 58,620 行。 译时间如下:

在调试模式下,ldc2 需要 3 分钟才能完成编译,内存使用量峰值约为 8GB,如果打开浏览器,我笔记本电脑的 16GB 内存很容易就饱和了。发布模式更糟糕,约为 11.5GB。

为了记录移植进度,我绘制了如下代码库的示意图:

如果一切按计划进行,上面两张图中的颜色都会改变,我非常期待!

最后,我来说一下我期待的效果:

☞天涯论坛关闭发帖!中国互联网的青春没了;iPhone 15 将全系支持灵动岛;美团成立机器人研究院 |极客头条

☞ 区块链创新之路,该何去何从?

☞Linus Torvalds 再次“警告”内核开发者:不要在圣诞节前夕提交代码

本文来自网络,不代表本站立场,转载请注明出处:https:

缺点,语言,Jai,代码,错误,问题,编程,Windows,调试信息,移植,代码库,语言,编译器,代码,bug

同城58网后续将为您提供丰富、全面的关于缺点,语言,Jai,代码,错误,问题,编程,Windows,调试信息,移植,代码库,语言,编译器,代码,bug内容,让您第一时间了解到关于缺点,语言,Jai,代码,错误,问题,编程,Windows,调试信息,移植,代码库,语言,编译器,代码,bug的热门信息。小编将持续从百度新闻、搜狗百科、微博热搜、知乎热门问答以及部分合作站点渠道收集和补充完善信息。