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. 调用其他包的代码

目录结构

  • Go%E8%B0%83%E7%94%A8C%20C++(cgo)%20e227f44812f248728480e093c7caf654/Untitled.png

  • 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))
    }
    
    

3. Go 和 C/C++ 混合编译


Go语言高级编程

CGO是C语言和Go语言之间的桥梁,原则上无法直接支持C++的类。CGO不支持C++语法的根本原因是C++至今为止还没有一个二进制接口规范(ABI)。一个C++类的构造函数在编译为目标文件时如何生成链接符号名称、方法在不同平台甚至是C++的不同版本之间都是不一样的。但是C++是兼容C语言,所以我们可以通过增加一组C语言函数接口作为C++类和CGO之间的桥梁,这样就可以间接地实现C++和Go之间的互联。当然,因为CGO只支持C语言中值类型的数据类型,所以我们是无法直接使用C++的引用参数等特性的。

  • 目录结构

    Go%E8%B0%83%E7%94%A8C%20C++(cgo)%20e227f44812f248728480e093c7caf654/Untitled%201.png

  • 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++动态库

  • 目录结构