Go调用C/C++(cgo)
1. 直接调用代码
package main
/*
#include <stdio.h>
// 结构体
typedef struct
{
char * name;
int age;
//double d;
}Person;
// 函数调用传参
void print(Person person)
{
printf("i am %s, %d age!\\n", person.name, person.age);
}
int hello(char * str)
{
printf("hello %s\\n", str);
return 123;
}
*/
import "C"
import "fmt"
func main() {
// 创建一个Person结构体
a := C.Person{}
a.name = C.CString("go")
a.age = 12
C.print(a)
str := C.CString("golang!")
ret := C.hello(str)
fmt.Printf("%#v\\n", a) // 有4字节内存对齐?
fmt.Println(ret)
}
import "C"
上面不能有空行- 如果C函数中没有提供结构体初始化方法,需要对字段一个一个赋值
2. 调用其他包的代码
目录结构
-
helper 包中的代码
package helper import "C" type CChar C.char func (p *CChar) GoString() string { return C.GoString((*C.char)(p)) } func PrintCString(cs *CChar) { println(cs.GoString()) }
-
main.go 代码
- main 包中的
C.char
类型和 helper 包中的C.char
是不同的类型,不可以直接传参! - Go 中无法引用 C 中的 静态变量
undefined reference to 'cs'
,暂时不知道原因
package main /* char* cs = "hello"; // 不能写成静态的 */ import "C" import ( "cgo/demo2/helper" "fmt" ) func main() { //s := (*helper.CChar)(C.cs) //C.CString() fmt.Printf("%#v\\n", C.cs) helper.PrintCString((*helper.CChar)(C.cs)) }
- main 包中的
3. Go 和 C/C++ 混合编译
CGO是C语言和Go语言之间的桥梁,原则上无法直接支持C++的类。CGO不支持C++语法的根本原因是C++至今为止还没有一个二进制接口规范(ABI)。一个C++类的构造函数在编译为目标文件时如何生成链接符号名称、方法在不同平台甚至是C++的不同版本之间都是不一样的。但是C++是兼容C语言,所以我们可以通过增加一组C语言函数接口作为C++类和CGO之间的桥梁,这样就可以间接地实现C++和Go之间的互联。当然,因为CGO只支持C语言中值类型的数据类型,所以我们是无法直接使用C++的引用参数等特性的。
-
目录结构
-
C++ MyBuffer 类
// my_buffer.h #pragma once #include <string> class MyBuffer { std::string* s_; public: MyBuffer(int size); ~MyBuffer(); int Size() const; char* Data(); }; // my_buffer.cpp #include "my_buffer.h" MyBuffer::MyBuffer(int size) { this->s_ = new std::string(size, char('\\0')); } MyBuffer::~MyBuffer() { delete this->s_; } int MyBuffer::Size() const { return this->s_->size(); } char* MyBuffer::Data() { return (char*)this->s_->data(); }
-
用纯 C 函数接口封装 C++ 类
// my_buffer_capi.h #pragma once typedef struct MyBuffer_T MyBuffer_T; // 构建/析构 对象 MyBuffer_T* NewMyBuffer(int size); void DeleteMyBuffer(MyBuffer_T* p); // 方法 char* MyBuffer_Data(MyBuffer_T* p); int MyBuffer_Size(MyBuffer_T* p); // my_buffer_capi.cpp #include "my_buffer.h" // 这里需要用C包含头文件,避免使用C++特性 extern "C" { #include "my_buffer_capi.h" } // 这里继承是为了在头文件中声明,C++为class不能声明在C头文件 struct MyBuffer_T: MyBuffer { MyBuffer_T(int size): MyBuffer(size) {} ~MyBuffer_T() {} }; MyBuffer_T* NewMyBuffer(int size) { auto p = new MyBuffer_T(size); return p; } void DeleteMyBuffer(MyBuffer_T* p) { delete p; } char* MyBuffer_Data(MyBuffer_T* p) { return p->Data(); } int MyBuffer_Size(MyBuffer_T* p) { return p->Size(); }
-
将纯 C 接口函数转为 Go 函数
//my_buffer_capi.go package capi /* #include "my_buffer_capi.h" */ import "C" type cgo_MyBuffer_T C.MyBuffer_T func cgo_NewMyBuffer(size int) *cgo_MyBuffer_T { p := C.NewMyBuffer(C.int(size)) return (*cgo_MyBuffer_T)(p) } func cgo_DeleteMyBuffer(p *cgo_MyBuffer_T) { C.DeleteMyBuffer((*C.MyBuffer_T)(p)) } func cgo_MyBuffer_Data(p *cgo_MyBuffer_T) *C.char { return C.MyBuffer_Data((*C.MyBuffer_T)(p)) } func cgo_MyBuffer_Size(p *cgo_MyBuffer_T) C.int { return C.MyBuffer_Size((*C.MyBuffer_T)(p)) }
-
包装为 Go 对象
// my_buffer.go package capi import "unsafe" type MyBuffer struct { cptr *cgo_MyBuffer_T } func NewMyBuffer(size int) *MyBuffer { return &MyBuffer{ cptr: cgo_NewMyBuffer(size), } } func (p *MyBuffer) Delete() { cgo_DeleteMyBuffer(p.cptr) } func (p *MyBuffer) Data() []byte { data := cgo_MyBuffer_Data(p.cptr) size := cgo_MyBuffer_Size(p.cptr) return ((*[1 << 31]byte)(unsafe.Pointer(data)))[0:int(size):int(size)] }
-
调用
package main //#include <stdio.h> import "C" import ( "demo3/capi" "fmt" "unsafe" ) func main() { buf := capi.NewMyBuffer(1024) defer buf.Delete() copy(buf.Data(), []byte("hello")) C.puts((*C.char)(unsafe.Pointer(&(buf.Data()[0])))) fmt.Println(string(buf.Data())) }
4. 调用C/C++动态库
- 目录结构