在新的 Go 语言中,如何调用 C++ 代码?换句话说,我如何包装我的 C++ 类并在 Go 中使用它们?
原文由 Frank 发布,翻译遵循 CC BY-SA 4.0 许可协议
我根据 Scott Wales 的回答 创建了以下示例。我已经在运行 go
版本 go1.10 darwin/amd64
的 macOS High Sierra 10.13.3 中对其进行了测试。
(1) library.hpp
的代码,我们打算调用的 C++ API。
#pragma once
class Foo {
public:
Foo(int value);
~Foo();
int value() const;
private:
int m_value;
};
(2) library.cpp
的代码,C++ 实现。
#include "library.hpp"
#include <iostream>
Foo::Foo(int value) : m_value(value) {
std::cout << "[c++] Foo::Foo(" << m_value << ")" << std::endl;
}
Foo::~Foo() { std::cout << "[c++] Foo::~Foo(" << m_value << ")" << std::endl; }
int Foo::value() const {
std::cout << "[c++] Foo::value() is " << m_value << std::endl;
return m_value;
}
(3) library-bridge.h
需要暴露一个 C
API 在 C++
中实现的 —b1bd3978c583b 可以使用它的 go
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
void* LIB_NewFoo(int value);
void LIB_DestroyFoo(void* foo);
int LIB_FooValue(void* foo);
#ifdef __cplusplus
} // extern "C"
#endif
(4) 代码为 library-bridge.cpp
,桥的实现。
#include <iostream>
#include "library-bridge.h"
#include "library.hpp"
void* LIB_NewFoo(int value) {
std::cout << "[c++ bridge] LIB_NewFoo(" << value << ")" << std::endl;
auto foo = new Foo(value);
std::cout << "[c++ bridge] LIB_NewFoo(" << value << ") will return pointer "
<< foo << std::endl;
return foo;
}
// Utility function local to the bridge's implementation
Foo* AsFoo(void* foo) { return reinterpret_cast<Foo*>(foo); }
void LIB_DestroyFoo(void* foo) {
std::cout << "[c++ bridge] LIB_DestroyFoo(" << foo << ")" << std::endl;
AsFoo(foo)->~Foo();
}
int LIB_FooValue(void* foo) {
std::cout << "[c++ bridge] LIB_FooValue(" << foo << ")" << std::endl;
return AsFoo(foo)->value();
}
(5) 最后, library.go
,调用C++ API的go程序。
package main
// #cgo LDFLAGS: -L. -llibrary
// #include "library-bridge.h"
import "C"
import "unsafe"
import "fmt"
type Foo struct {
ptr unsafe.Pointer
}
func NewFoo(value int) Foo {
var foo Foo
foo.ptr = C.LIB_NewFoo(C.int(value))
return foo
}
func (foo Foo) Free() {
C.LIB_DestroyFoo(foo.ptr)
}
func (foo Foo) value() int {
return int(C.LIB_FooValue(foo.ptr))
}
func main() {
foo := NewFoo(42)
defer foo.Free() // The Go analog to C++'s RAII
fmt.Println("[go]", foo.value())
}
使用以下 Makefile
liblibrary.so: library.cpp library-bridge.cpp
clang++ -o liblibrary.so library.cpp library-bridge.cpp \
-std=c++17 -O3 -Wall -Wextra -fPIC -shared
我可以按如下方式运行示例程序:
$ make
clang++ -o liblibrary.so library.cpp library-bridge.cpp \
-std=c++17 -O3 -Wall -Wextra -fPIC -shared
$ go run library.go
[c++ bridge] LIB_NewFoo(42)
[c++] Foo::Foo(42)
[c++ bridge] LIB_NewFoo(42) will return pointer 0x42002e0
[c++ bridge] LIB_FooValue(0x42002e0)
[c++] Foo::value() is 42
[go] 42
[c++ bridge] LIB_DestroyFoo(0x42002e0)
[c++] Foo::~Foo(42)
重要的
上面的注释 import "C"
在 go
程序 不是可选 的。您必须完全按照所示放置它们,以便 cgo
知道要加载哪个头文件和库,在这种情况下:
// #cgo LDFLAGS: -L. -llibrary
// #include "library-bridge.h"
import "C"
原文由 Escualo 发布,翻译遵循 CC BY-SA 3.0 许可协议
7 回答5.3k 阅读
6 回答6.8k 阅读✓ 已解决
3 回答2k 阅读✓ 已解决
2 回答3.9k 阅读✓ 已解决
2 回答3.2k 阅读✓ 已解决
1 回答3.2k 阅读✓ 已解决
1 回答2.7k 阅读✓ 已解决
更新: 我已经成功地将一个小型测试 C++ 类与 Go 链接起来
如果你用 C 接口包装你的 C++ 代码,你应该能够用 cgo 调用你的库(参见
$GOROOT/misc/cgo/gmp
中的 gmp 示例)。我不确定 C++ 中的类的想法是否真的可以在 Go 中表达,因为它没有继承。
这是一个例子:
我有一个 C++ 类定义为:
我想在 Go 中使用它。我将使用 C 接口
(我使用
void*
而不是 C 结构,因此编译器知道 Foo 的大小)实现是:
完成所有这些后,Go 文件是:
我用来编译它的makefile是:
尝试使用以下方法对其进行测试:
您需要使用 make install 安装共享库,然后运行 make test。预期输出为: