0

0

在 Go 中调用 C++ 代码的完整指南

霞舞

霞舞

发布时间:2025-07-02 15:22:15

|

677人浏览过

|

来源于php中文网

原创

在 go 中调用 c++ 代码的完整指南

本教程详细介绍了如何在 Go 语言中调用 C++ 代码。通过 C 接口的桥梁,我们将展示如何封装 C++ 类,并在 Go 程序中使用它们。文章提供了完整的示例代码,包括 C++ 类的定义、C 接口的封装、Go 语言的调用以及相应的编译和测试步骤,帮助开发者理解和实践 Go 与 C++ 的混合编程。

Go 语言本身并不直接支持调用 C++ 代码,但它提供了 cgo 工具,允许 Go 程序调用 C 语言函数。因此,为了在 Go 中使用 C++ 代码,我们需要先将 C++ 代码封装成 C 接口。

封装 C++ 代码为 C 接口

这个过程的核心思想是,创建一个 C 接口作为 Go 和 C++ 之间的桥梁。这个接口将 C++ 类的功能暴露为 C 函数,然后 Go 程序就可以通过 cgo 调用这些 C 函数,从而间接使用 C++ 代码。

下面是一个具体的例子:

立即学习C++免费学习笔记(深入)”;

假设我们有一个简单的 C++ 类 cxxFoo

// foo.hpp
class cxxFoo {
public:
  int a;
  cxxFoo(int _a):a(_a){};
  ~cxxFoo(){};
  void Bar();
};

// foo.cpp
#include 
#include "foo.hpp"
void cxxFoo::Bar(void) {
  std::cout << this->a << std::endl;
}

我们需要创建一个对应的 C 接口:

// foo.h
#ifdef __cplusplus
extern "C" {
#endif

typedef void* Foo;

Foo FooInit(void);
void FooFree(Foo);
void FooBar(Foo);

#ifdef __cplusplus
}
#endif

这里的 Foo 类型被定义为 void*,这是因为 C 语言中没有类似于 C++ 类的概念。使用 void* 可以隐藏 C++ 类的具体实现细节,避免在 C 接口中暴露 C++ 的类型信息。

C 接口的实现如下:

// cfoo.cpp
#include "foo.hpp"
#include "foo.h"

Foo FooInit() {
  cxxFoo *ret = new cxxFoo(1);
  return (void*)ret;
}

void FooFree(Foo f) {
  cxxFoo *foo = (cxxFoo*)f;
  delete foo;
}

void FooBar(Foo f) {
  cxxFoo *foo = (cxxFoo*)f;
  foo->Bar();
}

这个 C++ 代码实现了 C 接口中定义的函数,它负责创建、销毁和调用 C++ 类的成员函数。

在 Go 中调用 C 接口

有了 C 接口之后,我们就可以在 Go 程序中使用 cgo 来调用这些函数了。

盛世企业网站管理系统1.1.2
盛世企业网站管理系统1.1.2

免费 盛世企业网站管理系统(SnSee)系统完全免费使用,无任何功能模块使用限制,在使用过程中如遇到相关问题可以去官方论坛参与讨论。开源 系统Web代码完全开源,在您使用过程中可以根据自已实际情况加以调整或修改,完全可以满足您的需求。强大且灵活 独创的多语言功能,可以直接在后台自由设定语言版本,其语言版本不限数量,可根据自已需要进行任意设置;系统各模块可在后台自由设置及开启;强大且适用的后台管理支

下载
// foo.go
package foo

/*
#include "foo.h"
*/
import "C"
import "unsafe"

type GoFoo struct {
    foo C.Foo
}

// New 创建一个新的 C++ 对象并返回封装后的 Go 结构体
func New() GoFoo {
    var ret GoFoo
    ret.foo = C.FooInit()
    return ret
}

// Free 释放 C++ 对象占用的内存
func (f GoFoo) Free() {
    C.FooFree(unsafe.Pointer(f.foo))
}

// Bar 调用 C++ 对象的 Bar 方法
func (f GoFoo) Bar() {
    C.FooBar(unsafe.Pointer(f.foo))
}

在 Go 代码中,我们首先通过注释中的 #include "foo.h" 引入了 C 接口的头文件。然后,我们定义了一个 GoFoo 结构体,它包含一个 C.Foo 类型的字段,用于存储 C++ 对象的指针。

接下来,我们定义了 NewFreeBar 等方法,这些方法分别调用了 C 接口中的 FooInitFooFreeFooBar 函数。注意,由于 C 接口中使用的是 void* 类型,因此我们需要使用 unsafe.PointerC.Foo 类型转换为 unsafe.Pointer 类型,以便在 Go 和 C 之间传递指针。

编译和测试

为了编译和测试这个例子,我们需要创建一个 makefile 文件:

# makefile
TARG=foo
CGOFILES=foo.go
include $(GOROOT)/src/Make.$(GOARCH)
include $(GOROOT)/src/Make.pkg

foo.o: foo.cpp
    g++ $(_CGO_CFLAGS_$(GOARCH)) -fPIC -O2 -o $@ -c $(CGO_CFLAGS) $<

cfoo.o: cfoo.cpp
    g++ $(_CGO_CFLAGS_$(GOARCH)) -fPIC -O2 -o $@ -c $(CGO_CFLAGS) $<

CGO_LDFLAGS += -lstdc++

$(elem)_foo.so: foo.cgo4.o foo.o cfoo.o
    gcc $(_CGO_CFLAGS_$(GOARCH)) $(_CGO_LDFLAGS_$(GOOS)) -o $@ $^ $(CGO_LDFLAGS)

这个 makefile 文件定义了编译 C++ 代码和链接 Go 代码的步骤。我们需要先编译 C++ 代码,然后将编译后的目标文件链接成一个共享库。

最后,我们可以创建一个测试文件来测试我们的代码:

// foo_test.go
package foo

import "testing"

func TestFoo(t *testing.T) {
    foo := New()
    foo.Bar()  // 应该输出 1
    foo.Free()
}

运行 make test 命令,如果一切顺利,我们应该能够看到以下输出:

gotest
rm -f _test/foo.a _gotest_.6
6g -o _gotest_.6 foo.cgo1.go foo.cgo2.go foo_test.go
rm -f _test/foo.a
gopack grc _test/foo.a _gotest_.6  foo.cgo3.6
1
PASS

注意事项

  • 内存管理:在使用 cgo 调用 C++ 代码时,需要注意内存管理。由于 C++ 代码和 Go 代码运行在不同的内存空间中,因此我们需要手动管理 C++ 对象的内存。在 Go 代码中创建的 C++ 对象,需要在 Go 代码中显式地释放。

  • 异常处理:C++ 异常不能跨越 cgo 的边界。如果 C++ 代码抛出了异常,这个异常不会传递到 Go 代码中。因此,我们需要在 C++ 代码中捕获异常,并将其转换为 C 接口可以处理的错误码。

  • 继承关系:Go 语言没有继承的概念,因此在封装 C++ 类时,需要特别注意继承关系的处理。一种常见的做法是将 C++ 类的继承关系转换为 Go 语言的组合关系。

总结

通过 C 接口,我们可以在 Go 语言中调用 C++ 代码。这种方法虽然比较繁琐,但是它提供了一种在 Go 程序中使用现有 C++ 代码的有效途径。在实际应用中,我们需要根据具体的需求选择合适的封装方式,并注意内存管理和异常处理等问题。

相关专题

更多
golang结构体相关大全
golang结构体相关大全

本专题整合了golang结构体相关大全,想了解更多内容,请阅读专题下面的文章。

194

2025.06.09

golang结构体方法
golang结构体方法

本专题整合了golang结构体相关内容,请阅读专题下面的文章了解更多。

186

2025.07.04

javascriptvoid(o)怎么解决
javascriptvoid(o)怎么解决

javascriptvoid(o)的解决办法:1、检查语法错误;2、确保正确的执行环境;3、检查其他代码的冲突;4、使用事件委托;5、使用其他绑定方式;6、检查外部资源等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

173

2023.11.23

java中void的含义
java中void的含义

本专题整合了Java中void的相关内容,阅读专题下面的文章了解更多详细内容。

92

2025.11.27

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

990

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

50

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

232

2025.12.29

C++类型转换方式
C++类型转换方式

本专题整合了C++类型转换相关内容,想了解更多相关内容,请阅读专题下面的文章。

290

2025.07.15

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

74

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
React 教程
React 教程

共58课时 | 3.2万人学习

Pandas 教程
Pandas 教程

共15课时 | 0.9万人学习

ASP 教程
ASP 教程

共34课时 | 3.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号