对,没有看错,在C/C++项目中使用Go代码!

试想一下,已经有了一个熟悉且稳定的某Go库,而在C/C++项目中正好需要它的功能,且这部分并非性能敏感的。显然的,此Go库用在这个C/C++项目,比再从头用C/C++造个轮子,成本、风险均更小更可控。这样的思路和态度对把996工作方式降级为965,具有现实可操作性。

我用Go实现的高性能多组Raft库Dragonboat就自带C++支持,可直接用于C++项目。比如共识和一致性的需求是来自诸如系统配置数据之类的非核心部分,或者说系统里有一致性要求的吞吐确定不高,比如Redis那样一秒才10万次左右的吞吐,那完全可以让这样的Go库在C++项目里直接使用,从而把精力真正用到C++做好其核心的部分。

这里就用Dragonboat做例子,详解具体步骤、性能预期以及一路走来的各坑。Dragonboat项目自带在C++项目中使用的例程,欢迎试用后点Star支持:

lni/dragonboat​github.com

指针的限制

C++内存与Go内存相比较,两者在申请、使用、回收机制上均有显著差别。在C++项目中使用Go代码,使得这两种类型的内存在一个应用程序里同时出现,且均由使用者来操作,那么保证它们的正确性就显然严重违背了本面向偷懒的编程方法中偷懒这一核心目的。官方的cgo文档在Passing Pointers一节倒是对具体的详细的要求有完整定义和介绍:

https://golang.org/cmd/cgo/#hdr-Passing_pointers​golang.org

简单来说,Golang出于GC的考虑,在你向C/C++代码传递Go指针的时候,不允许该指针指向的内存区域那含有别的Go指针。既然是面向偷懒的编程,肯定不能学文档里的这种死规矩做,何况因为程序需要,有时候必须得传递这样的指针所指向的Go对象怎么办?

Dragonboat中的做法是不传递任何Go指针,所有的Go对象,全部转换为interface{}以后保存在一个map[uint64]interface{}中,对每个新建的Go对象赋予一个全局唯一的uint64,使之成为该Go对象的handler,通过C++/Go程序之间传递这个uint64值,来达到传递这个Go对象的效果。也就是说,实际上的Go指针从不跨越Go/C++的边界。

恭喜!只要不钻牛角尖,不死命地去想所谓的“性能”、“代码是否优雅”问题,愿意接受上述方案,就已成功绕开所有在C/C++中使用Go的语言层面的坑了。

导出接口

为了让C/C++代码使用,我们需要把Go所实现的功能导出,并且导出的这些调用接口,应该考虑了上一节介绍的内存指针方面的特殊设计。具体要做的是给已有的Go库做一个wrapper,包装出这样一套供C/C++使用的接口。

举个例子,Dragonboat中,NodeHost这个facade interface结构体向应用提供所有Dragonboat库所支持的功能API,每个应用进程通常创建、持有并使用一个这样的NodeHost实例。那在wrapper中,我们会有如下这个NewNodeHost函数用以创建NodeHost实例:

//export NewNodeHost

其中"//export NewNodeHost"表示NewNodeHost函数需要被导出,addManagedObject将所创建的Go管理下的NodeHost对象加入上一节提到的map[uint64]interface{}容器中,并返回一个uint64值给调用者。因为这是一个已导出的函数,它的调用者就是C/C++代码。

需要使用这个NodeHost的时候,只要提供上面NewNodeHost返回的uint64值即可,比如当我们需要停止这个NodeHost工作时,我们提供了如下导出的函数:

//export StopNodeHost

从中可见,跨越Go/C++边界传递的,都是内建类型的值,所有的真正的Go调用实际均发生在Go一侧。C++部分只是保存一堆用于标示各Go对象的uint64类型的oid值。当某对象不再需要的时候,在它们对应的C++对象的析构函数中调用RemoveManagedObject()便可。

//export RemoveManagedObject

对所有需要导出的函数如法炮制,1天左右就可以把数万行几十个接口函数的复杂Go模块封装好了。具体其它细节,比如如何来回传递别的内建类型的参数,可查看Dragonboat的代码:

https://github.com/lni/dragonboat/blob/master/binding/binding.go​github.com

C/C++中使用上述Go库

接着以C-shared模式,编译出一个可供C/C++程序使用的的shared library,请注意其中“-buildmode=c-shared”部分。

=

上述命令执行以后,会生成binding/libdragonboat.so这个shared library和libdragonboat.h这个头文件。

有了上述的binding/libdragonboat.h与binding/libdragonboat.so就可以直接在C/C++项目中使用Go所现实的库了,和使用一个C实现的.so形式的shared library并无太多本质区别。Go的runtime的启动、关闭,协程管道等等内建类型的用,GC那一大堆麻烦事,都会对C/C++全透明。

面向偷懒的编程至此就基本达成了。

完整的示例可以参考Dragonboat所附带的C++ Hello World例程:

lni/dragonboat​github.com

性能

虽说是面向偷懒的编程方法,但其实稍加必要优化以后,性能依旧是不错的。以Dragonboat为例,使用C++20尚未标准化的一个协程库,16字节的负载下,在三台22核2.8GHz的志强服务器上,一秒百万级的写操作很轻松。对标github上纯C++的Raft库,这个性能在当前依旧是很不俗的。

当然,具体的绝对性能有多高并不是这里追求的。把项目非核心部分转到Go的实现上,更方便集中资源用C++处理系统中真正核心的部分,这才是本面向偷懒的编程方法真正的目的所在。

c++ map 析构函数_面向偷懒的编程 - C/C++项目中使用Go的分布式系统库相关推荐

  1. c++ map 析构函数_说说C++的虚析构函数

    首先看一段示例代码: class 上面代码中 class AX 的析构函数不会被调用,如果在 AX 析构函数中需要进行一些资源释放工作(一般都是如此)则这些工作不会被执行.解决的方法就是将基类析构函数 ...

  2. java 代码造假_老板居然让我在Java项目中“造假”

    1. 前言 老板说,明天甲方要来看产品,你得造点数据,而且数据必须是"真"的,演示效果要好看一些,这样他才会买我们的产品,我好明年给你换个嫂子.一般开发接到这种过分要求都不会很乐意 ...

  3. Java新手造假_老板居然让我在Java项目中“造假”

    1. 前言 老板说,明天甲方要来看产品,你得造点数据,而且数据必须是"真"的,演示效果要好看一些,这样他才会买我们的产品,我好明年给你换个嫂子.一般开发接到这种过分要求都不会很乐意 ...

  4. jacoco测试代码覆盖率_使用Jacoco测量多模块Android项目中的单元测试覆盖率:第2部分

    jacoco测试代码覆盖率 In the first article, we discovered one of the two key Gradle commands which come with ...

  5. pythondjango项目集成_[Python]将Wagtail整合到Django2项目中

    Django是Python主流的Web框架之一,目前主要版本是Django 2.1,但是很多扩展都是基于Django 1.x.最近想做个简单的Web发布,选择了Wagtail,因为其他两个主流的cms ...

  6. 中使用js修改变量值_谈一谈css-in-js在React项目中的使用

    一.什么是css-in-js 参考:[css in js 简介] 简单来说,传统的前端方案推崇"关注点分离"原则,HTML.CSS.JavaScript 应该各司其职,进行分离. ...

  7. 建模大师怎么安装到revit中_全面解析Revit软件在装配式建筑项目中的建模思路...

    Revit软件在装配式建筑项目中的建模思路 Revit是Autodesk公司出品的一套软件,可帮助建筑设计师设计.建造和维护质量更好.能效更高的建筑,在建筑行业中的应用非常广泛. 在2019版的Rev ...

  8. python安装django模块_在您的(Django)项目中使用setup.py

    Python部落(python.freelycode.com)组织翻译,禁止转载,欢迎转发. 最近一位客户问我为什么我们创建的Django项目的根目录下都有一个setup.py.其他很多项目没有set ...

  9. java maven log4j配置_如何在Maven构建的Java项目中使用log4j

    参考: 说明: 本文仅仅作为项目开发过程中的记录,不提供详细的说明.按照步骤直接复制粘贴即可成功使用. 第一步:添加依赖 log4j log4j 1.2.17 第二步:添加配置文件 文件位置说明:直接 ...

  10. androidstudio使用mac字体_如何在Android Studio编写的项目中使用自定义字体

    有很多方法可以在字段上设置自定义字体系列,我使用如下所示. 要将字体添加为资源,请在Android Studio中执行以下步骤: 1)右键单击res文件夹,然后转到新建> Android资源目录 ...

最新文章

  1. GIA张怡:关于小白入门AI算法工程师的直播分享
  2. ping -c3 baidu.com  ping过去是这样,代表网络畅通
  3. c语言中指针往内存写值,C语言编程常见问题解答之指针和内存分配
  4. Ubuntu系统下实时监控GPU的温度
  5. 有名内部类和匿名内部类的用法
  6. Spring异步任务处理,@Async的配置和使用
  7. fastDFS配置nginx
  8. 锦天科技被盛大收购 23岁创始人成亿万富翁
  9. QScrollArea使用详解
  10. TortoiseGit 如何使用 cherry-pick
  11. oracle返回工作日的函数,oracle计算一个日期加上指定工作日(排除周六周日和一系列节假日)时间...
  12. 台式机安装双系统win10+Ubuntu
  13. 玩转Luat——导读
  14. 手把手教你在Linux上上搭建BitTorrent服务器
  15. linux掩码,linux文件权限掩码umask
  16. 基于ssm的大学校园兼职平台系统
  17. NGS测序嵌合体是个需要去除的错误扩增序列
  18. 基于Proteus学习单片机系列(七)——AD转换及其应用--ADC0832
  19. 硬件测试中环境试验中各测试项的要求
  20. 图片转素描(初级板)

热门文章

  1. 苹果开发者被盯上了!Xcode又双叒叕被攻击了
  2. 由争议拼多多之货找人想到的 BlockChain Storage 之5、区块链存储 - 存储供需的智能匹配...
  3. 中文版边缘计算白皮书发布,引领行业新趋势
  4. 常见花材的固定的方法有哪些_固定无梁拱形屋顶的方法都有哪些呢?
  5. 【指纹识别】基于matlab GUI指纹识别门禁系统【含Matlab源码 1692期】
  6. 【优化覆盖】基于matlab萤火虫算法求解无线网络传感覆盖优化问题【含Matlab源码 1275期】
  7. 【基础教程】基于matlab图像处理(表示方法+数据结构+基本格式+类型转换+读取+点运算+代数运算)【含Matlab源码 834期】
  8. 【优化算法】磷虾群算法(KH)【含matlab源码 133期】
  9. 【数学建模】基于matlab时变参数随机波动率向量自回归模型(TVP-VAR)【含Matlab源码 037期】
  10. 监听程序当前无法识别连接描述符中请求的服务_Linux I/O复用中select poll epoll模型的介绍及其优缺点的比较...