工作上遇到一个需求,需要把一个C++的动态库的功能封装为Web接口。由于没有C++开发经验,C有点经验,于是考虑了两种方案:
- 封装为PHP扩展
- 在Golang中使用CGO
两种方案我都可以做,但最终决定采用第2种方案,主要考虑的因素是这个Web服务最终需要在客户那里进行私有化部署,采用PHP的话,部署的时候还需要Nginx、Fpm(当然也可以直接用Swoole),但是PHP代码是明文的,虽然可以买一些商业软件进行加密(比如Swoole Compiler)。如果直接用Golang的话,就可以直接给用户部署一个二进制程序(需要strip掉符号信息)就可以了,部署起来更方便。
下面将通过一个示例程序,演示如何在Golang中通过cgo调用C++。
示例代码目录:
1 | 复制代码. |
c_src.h 源码:
1 | 复制代码#ifndef WRAP_CPP_H |
extern "C"
作用:Combining C++ and C - how does #ifdef __cplusplus work?
c_src.cpp 源码:
1 | 复制代码#include "src.hpp" |
c_src.cpp 可能的疑问:
- 为何需要定义Foo?因为在C中没有Class的概念,所以需要把C++的Class转换为C中的数据类型
- 为何在FooGetName中需要进行malloc和memcpy?因为name是局部变量,并且内存分配在栈上,当cgo调用返回后,name所占用的内存会被释放掉。
main.go 源码:
1 | 复制代码package main |
main.go 可能的疑问:
- unsafe.Pointer(…)相当于把变量强转为C中的void*类型
- SetName中为何需要做转换,因为name变量的内存是在Golang中分配的,且string类型是不可修改的,因此,需要在c中分配name所需要的内存,以便在FooSetName中使用
- 需要注意的一点是
import "C"
上面必须紧跟// #include ...
注释
src.hpp 源码:
1 | 复制代码#ifndef CXX_H |
src.cpp 源码
1 | 复制代码#include "src.hpp" |
小结:
- C中的数据类型会与Golang的C.xxx数据类型对应:CGO 类型(CGO Types)
- 在C/C++中申请的内存,就得在C/C++中释放
- 对于需要链接C/C++动态库,或加上编译参数,可以在
import "C"
加上对应注释// #cgo CFLAGS: -DPNG_DEBUG=1
参考资料:
- How to use C++ in Go?
- Command cgo
- C? Go? Cgo!
- Golang CGO编程之调用返回char*指针及长度的C函数库
- CGO: Go与C互操作技术(一):Go调C基本原理
本文转载自: 掘金