Golang中CGO与C++绑定的实现探讨

Golang中CGO与C++绑定的实现探讨 我正在尝试用Go语言编译简单的C++代码,但遇到了一些错误:

// namespace test
// {
//     int val = 500;
// }
//
import "C"

输出:error: unknown type name ‘namespace’ 这很可能是因为编译器是针对C语言而不是C++的;我该如何更改这一点?

// #include <algorithm>
import "C"

输出:fatal error: algorithm: No such file or directory 包含文件存在,并且可以被g++找到;但Go编译器找不到该文件。我需要在这里更改什么?我不想手动包含每个文件夹。 这是一个位于/usr/include/c++/8/algorithm的C++头文件;这些头文件没有被包含是因为它是C语言而不是C++吗?

我已经看过howto-go-with-cpp,但这种变通方法对我的构建堆栈和使用的依赖项并不好,因为它需要修改包含的头文件。

Go版本:在树莓派上使用go1.11.6 linux/arm


更多关于Golang中CGO与C++绑定的实现探讨的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

另一个例子使用了我发布的链接中相同的技术:他们创建了一个额外的C头文件,并将C++文件编译为库,这正是我试图避免的。

更多关于Golang中CGO与C++绑定的实现探讨的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go中使用CGO调用C++代码需要特殊处理,因为CGO默认只支持C语言。以下是解决方案:

1. 启用C++支持

使用// #cgo CFLAGS:// #cgo CXXFLAGS:指令启用C++编译:

// #cgo CXXFLAGS: -std=c++11
// #cgo LDFLAGS: -lstdc++
/*
#include <cstdio>

namespace test {
    int val = 500;
}

extern "C" {
    int getVal() {
        return test::val;
    }
}
*/
import "C"
import "fmt"

func main() {
    fmt.Println(C.getVal()) // 输出: 500
}

2. 处理C++标准库头文件

对于<algorithm>等C++标准库头文件,需要使用C++兼容的包含方式:

// #cgo CXXFLAGS: -std=c++11 -I/usr/include/c++/8 -I/usr/include/arm-linux-gnueabihf/c++/8
// #cgo LDFLAGS: -lstdc++
/*
#include <algorithm>
#include <vector>

extern "C" {
    void sortExample() {
        std::vector<int> vec = {3, 1, 4, 1, 5};
        std::sort(vec.begin(), vec.end());
    }
    
    int maxExample(int a, int b) {
        return std::max(a, b);
    }
}
*/
import "C"

func main() {
    C.sortExample()
    maxVal := C.maxExample(10, 20)
    println(maxVal) // 输出: 20
}

3. 完整的C++类绑定示例

// #cgo CXXFLAGS: -std=c++11
// #cgo LDFLAGS: -lstdc++
/*
#include <cstdlib>

class Counter {
private:
    int count;
public:
    Counter() : count(0) {}
    void increment() { count++; }
    int get() { return count; }
};

// C包装函数
extern "C" {
    Counter* createCounter() {
        return new Counter();
    }
    
    void incrementCounter(Counter* c) {
        c->increment();
    }
    
    int getCounter(Counter* c) {
        return c->get();
    }
    
    void deleteCounter(Counter* c) {
        delete c;
    }
}
*/
import "C"
import (
    "fmt"
    "unsafe"
)

type Counter struct {
    ptr unsafe.Pointer
}

func NewCounter() *Counter {
    return &Counter{
        ptr: unsafe.Pointer(C.createCounter()),
    }
}

func (c *Counter) Increment() {
    C.incrementCounter((*C.Counter)(c.ptr))
}

func (c *Counter) Get() int {
    return int(C.getCounter((*C.Counter)(c.ptr)))
}

func (c *Counter) Close() {
    C.deleteCounter((*C.Counter)(c.ptr))
    c.ptr = nil
}

func main() {
    counter := NewCounter()
    defer counter.Close()
    
    counter.Increment()
    counter.Increment()
    fmt.Println(counter.Get()) // 输出: 2
}

4. 针对树莓派的特定配置

对于ARM架构的树莓派,需要指定正确的包含路径:

// #cgo CXXFLAGS: -std=c++11 -I/usr/include/c++/8 -I/usr/include/arm-linux-gnueabihf/c++/8
// #cgo LDFLAGS: -lstdc++
/*
#include <iostream>

extern "C" {
    void printHello() {
        std::cout << "Hello from C++" << std::endl;
    }
}
*/
import "C"

func main() {
    C.printHello()
}

5. 构建命令

使用go build时,确保C++编译器可用:

# 设置C++编译器
export CXX=g++
export CC=gcc

# 构建
go build -x main.go

关键点:

  1. 必须使用extern "C"包装C++函数,避免名称修饰
  2. 通过// #cgo CXXFLAGS:指定C++编译标志
  3. 链接-lstdc++标准C++库
  4. 对于C++类,需要在Go中创建包装结构体管理生命周期
  5. 使用unsafe.Pointer处理C++对象指针
回到顶部