原文链接: Golang 中通过 cgo 调用 C++ 的动态库的功能封装
将C++warpper 文件写在go中 https://github.com/winlinvip/go-fdkaac/blob/master/fdkaac/dec.go
https://github.com/giorgisio/goav/blob/master/avfilter/avfilter.go
Examples of calls between Go and C/C++
Golang 中通过 cgo 调用 C++ 的动态库的功能封装
下面将通过一个示例程序,演示如何在 Golang 中通过 cgo 调用 C++。
extern "C" 的作用:
int f(void)示例代码目录:
.
├── bin
│ └── cgo
└── src
└── cgo
├── c_src.cpp // 在Golang中调用的C函数定义 使用C++ 编译器编译成`extern "C"`兼容格式的文件
├── c_src.h // C头文件,声明了哪些C函数会在Golang中使用,在main.go中包含
├── main.go
├── src.cpp // C++代码
└── src.hpp // C++头文件
c_src.h 源码:
#ifndef WRAP_CPP_H
#define WRAP_CPP_H
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
typedef void * Foo;
Foo FooNew();
void FooDestroy(Foo f);
const char* FooGetName(Foo f, int* retLen);
void FooSetName(Foo f, char* name);
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // WRAP_CPP_H
c_src.cpp warpper 源码:
#include "src.hpp"
#include "c_src.h"
#include <cstring>
// 返回c++ Foo对象,但转换为C的 void*
Foo FooNew()
{
cxxFoo* ret = new cxxFoo("rokety"); // 创建 c++ 对象,并返回 void * 类型
return (void*)ret;
}
void FooDestroy(Foo f)
{
cxxFoo* foo = (cxxFoo*)f; // 把 void * 强转回 c++ 类型并释放。
delete foo;
}
// 封装cxxFoo的get_name方法
const char* FooGetName(Foo f, int* ret_len)
{
cxxFoo* foo = (cxxFoo*)f;
std::string name = foo->get_name();
*ret_len = name.length();
const char* ret_str = (const char*)malloc(*ret_len);
memcpy((void*)ret_str, name.c_str(), *ret_len);
return ret_str;
}
// 封装cxxFoo的set_name方法
void FooSetName(Foo f, char* name)
{
cxxFoo* foo = (cxxFoo*)f;
std::string _name(name, strlen(name));
foo->set_name(_name);
}
c_src.cpp 可能的疑问:
- 为何需要定义 Foo?因为在 C 中没有 Class 的概念,所以需要把 C++ 的 Class 转换为 C 中的数据类型
- 为何在 FooGetName 中需要进行 malloc 和 memcpy?因为 name 是局部变量,并且内存分配在栈上,当 cgo 调用返回后,name 所占用的内存会被释放掉。
main.go 源码:
package main
// #include "c_src.h"
// #include <stdlib.h>
import "C"
import (
"fmt"
"unsafe"
)
type GoFoo struct {
foo C.Foo
}
func NewGoFoo() GoFoo {
var ret GoFoo
ret.foo = C.FooNew()
return ret
}
func (f GoFoo) Destroy() {
C.FooDestroy(f.foo)
}
func (f GoFoo) GetName() string {
rLen := C.int(0)
name := C.FooGetName(f.foo, &rLen)
defer C.free(unsafe.Pointer(name)) // 必须使用C的free函数,释放FooGetName中malloc的内存
return C.GoStringN(name, rLen) // 从name构造出golang的string类型值
}
func (f GoFoo) SetName(name string) {
cname := C.CString(name) // 将golang的string类型值转换为c中的char*类型值,这里会调用到c的malloc
C.FooSetName(f.foo, cname)
C.free(unsafe.Pointer(cname)) // 释放上面malloc的内存
}
func main() {
foo := NewGoFoo()
fmt.Println(foo.GetName())
foo.GetName()
foo.SetName("new rokety")
fmt.Println(foo.GetName())
foo.Destroy()
}
main.go 可能的疑问:
import "C"// #include ...src.hpp 源码:
#ifndef CXX_H
#define CXX_H
#include <string>
class cxxFoo
{
public:
cxxFoo(std::string name);
~cxxFoo();
std::string get_name();
void set_name(std::string name);
private:
std::string name;
};
#endif // CXX_H
src.cpp 源码
#include "src.hpp"
#include <iostream>
cxxFoo::cxxFoo(std::string name)
{
this->name = name;
}
cxxFoo::~cxxFoo()
{
}
std::string cxxFoo::get_name()
{
return this->name;
}
void cxxFoo::set_name(std::string name)
{
this->name = name;
}
小结:
import "C"// #cgo CFLAGS: -DPNG_DEBUG=1参考资料: