首页 » 软件开发 » 我是如何把 5 万行 C++ 代码移植到 Go 的?(代码类型函数生成语言)

我是如何把 5 万行 C++ 代码移植到 Go 的?(代码类型函数生成语言)

落叶飘零 2024-07-24 07:17:09 0

扫一扫用手机浏览

文章目录 [+]

接下来让我们对比一下 Go 语言的优缺点。

优点

Emacs 开发平台

借助自动完成、跳转到定义、保存时的错误检查、智能重构和 GoTest 集成等插件,Emacs 成为了 Go 语言环境下最好的 IDE 工具。
另外,它也可以很方便地通过 Elisp 进行定制和扩展。
如果你本人恰好是 Emacs 的爱好者,这绝对是一个大大的加分项。

我是如何把 5 万行 C++ 代码移植到 Go 的?(代码类型函数生成语言) 软件开发
(图片来自网络侵删)

Goroutines(协程)

Go 实现了基于消息传递的并发,作者认为这是最简单的并发形式,使用也超级方便。
通过将 GOMAXPROCS 设置为 1,Go 还允许开发者通过使用与并发代码完全相同的方式来编写并行 / 异步代码。
与其它提供内置轻量级线程调度器的语言 Erlang/Elixir 和 Haskell 相比:前者缺乏静态类型,后者在实际开发中很少被管理人员采用。

没有继承

在很多情况下,基于继承的 OO(面向对象)是一种反模式,这些冗余和模糊的代码几乎没有什么好处,Go 则直接取消了这类代码。
这有可能也是 Rob Pike 等人设计 Go 的初衷:谷歌内部有一大堆类似于企业版本 Fizzbuzz 的 Java/C++ 代码,他们希望能从这些代码中彻底解放出来。
也就是说,尽管在旧的 C++ 服务端遗留代码中使用继承是合理的,但最好还是使用更现代的风格来重写代码,而且重写过程也并不复杂。

更好的可读性

Go 代码更易于阅读和理解。
相比之下,很多 C++ 代码需要几个小时才能完全理解。
Go 本身也促使开发者编写可读的代码:这种语言完全避免了下面这种自做聪明的情形:

“嘿,这篇论文(基本上没人读得懂)中的 >8=3 运算符可以让我节省 10 行代码,我最好把它写进代码里,我的同事也不难理解这行代码,因为它的意思已经在类型签名中很清楚地表达出来了(反正我是没看懂):(PrimMonad W, PoshFunctor Y, ReichsLens S) => W Y S((I -> W) -> Y) -> G -> Bool \"。

简单而规范的语法

当我们需要将一个封闭函数的名称添加到每个日志字符串的开头时,如果使用 Emacs,一个简单的regexp find-replace(正则表达式)命令就可以实现,而对于更复杂的语言则需要使用解析器。
不论是通过 Emacs 宏或者是 Go 模板,简单的语法可以更容易地生成代码。

Emacs+Go== 参数多态:我们可以使用 Emacs 宏来加速生成 Go 所需要的 \" 复制粘贴 \",而且,如果函数编写正确,那我们也可以用regex命令来更新所有的 \" 复制粘贴 \" 函数。
这样,我们就可以很容易地更新 fooInt、fooFloat 和 fooDouble 等函数,对比支持参数多态的语言对 foo 函数的更新,整个过程没有什么太大区别。
这样做的缺点是,虽然 Emacs 宏和regex命令可以编写和修改 Go 代码,但它仍然不如真正的多态实现那样简洁和易读;而且对于不熟悉 regex 以及可扩展编辑器(Emacs)的人来说,维护同样也不容易。

有效的内置模板

通过 Go 的文本 / 模板包,我们可以很容易地生成新代码。
它还允许开发者在生成代码时使用 IO:例如,有一个同某些特定服务交互的库,它通过 XML Schema 生成。
如果能够用不同的函数来生成不同的数据类型,那么就可以保证代码的类型安全。

在 C++ 中,IO 不能在编译时执行,因此不能使用上述模式来生成代码。
允许编译时使用 IO 的语言有:

F#,通过 TypeProviders 实现。
Idris,也使有 TypeProviders。
Lisp,可以在宏中执行 IO。
Haskell,它有一个编译期运行的函数 IO -> Q。
D,编译时可以使用“import”来读取文件。
Nimrod,有特殊的函数实现。
Elixir 或 Erlang,可以通过宏执行任意的 IO。
Rust,可以使用函数libsyntax在编译时执行任意的计算和 IO。
缺点

斯德哥尔摩综合征

前面已经提到,在允许使用 IO 的特性上,使用模板生成 Go 代码要比用 C++ 元编程好得多,而 C++ 元编程在这里显然是多余的,因为完全可以用另外一种可以支持 IO 的程序语言来生成代码。

没有实现参数多态

尽管很多人认为这在实践中并不是一个问题,但在这里,它是一个很严重的问题。
如果把新的 Go 代码再移植回 C++ 的话,考虑到 C++ 的函数多态和类型多态,代码量可能会减少到目前的一半,并且具有更好的类型安全。
如果用 Haskell 重写的话,代码量会更少,而使用 Clojure 的话,代码量有可能控制到 1000 行以内,当然这些代码可能很难被调试或维护。

牺牲了类型安全

针对服务器处理的各种 protobuffer messages(协议缓冲消息),我们使用了扩展属性的方式,作者最初打算为每一种消息设置一种扩展属性,这样 FooExtensionAttribute 就不能用在 Bar 函数上。
Go 并没有实现参数多态和泛型,这意味着将会产生大量的重复代码,所以最终只使用了一种 ExtensionAttribute,并且类型系统也没有检查它是否用于扩展合适的消息。

二进制文件太大

如果使用代码来生成类型安全的 API,并确保每种数据类型都有明确的类型访问器和诸如此类的东西,则很容易生成超过 10W 行的 Go 代码以及 30MB 以上的二进制文件,编译时间也会更长。
在这种情况下,一般会超过 10 秒。
当然,这不是一个很严重的问题,因为我们可以把代码编译成静态库,这只需要一次,之后就可以通过静态链接来访问了。

内核兼容性有待提高

很多时候由于各种无奈的原因,需要把代码部署到一个旧内核上。
而且,如果这个内核不支持最新的 Go 版本,就不得不换到一个旧的、很慢的 Go 版本,这多少有些令人沮丧。

结语

Go 语言是一把双刃剑:它禁止一切复杂的抽象,不管是优秀的抽象亦或是很差的抽象。
如果你和你的同事正在使用很糟糕的抽象,那切换到不能使用抽象的 Go 语言自然很好,反之亦然。
当然这也要取决于判断抽象好坏的标准。

查看英文原文:

https://togototo.wordpress.com/2015/03/07/fulfilling-a-pikedream-the-ups-of-downs-of-porting-50k-lines-of-c-to-go/

标签:

相关文章

语言中的借用,文化交融的桥梁

自古以来,人类社会的交流与发展离不开语言的传播。在漫长的历史长河中,各民族、各地区之间的文化相互碰撞、交融,产生了许多独特的语言现...

软件开发 2025-01-01 阅读1 评论0

机顶盒协议,守护数字生活的新卫士

随着科技的飞速发展,数字家庭逐渐走进千家万户。在这个时代,机顶盒成为了连接我们与丰富多彩的数字世界的重要桥梁。而机顶盒协议,作为保...

软件开发 2025-01-01 阅读1 评论0

语言基础在现代社会的重要性及方法步骤

语言是人类沟通的桥梁,是社会发展的基础。语言基础作为语言学习的基石,对于个人、社会乃至国家的发展具有重要意义。本文将从语言基础在现...

软件开发 2025-01-01 阅读2 评论0

粤语电影,传承文化,点亮时代之光

粤语电影,作为中国电影产业的一朵奇葩,以其独特的地域特色、丰富的文化内涵和鲜明的艺术风格,赢得了广大观众的喜爱。本文将从粤语电影的...

软件开发 2025-01-01 阅读3 评论0

苹果游戏语言,塑造未来娱乐体验的基石

随着科技的飞速发展,游戏产业逐渐成为全球娱乐市场的重要支柱。在我国,游戏产业更是蓬勃发展,吸引了无数玩家和投资者的目光。而在这其中...

软件开发 2025-01-01 阅读1 评论0