在新的Go语言中,我如何调用c++代码?换句话说,我如何包装我的c++类并在Go中使用它们?
当前回答
有趣的是,这一声明提到了许多更广泛的问题。Dan Lyke在他的网站Flutterby上进行了一场非常有趣和深思熟虑的讨论,讨论了开发进程间标准作为引导新语言的一种方式(以及其他分支,但这是与本文相关的)。
其他回答
这可以使用命令cgo来实现。
在本质上 如果导入“C”之前紧接着有一个注释,这个注释被称为preamble,在编译包的C部分时被用作头文件。例如:“ 来源:https://golang.org/cmd/cgo/
// #include <stdio.h>
// #include <errno.h>
import "C"
我根据Scott Wales的回答创建了下面的例子。我已经在macOS High Sierra 10.13.3运行go版本go1.10 darwin/amd64上测试了它。
(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++实现的C API,以便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)最后,图书馆。go程序调用c++ API。
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)
重要的
上面在go程序中导入“C”的注释是不可选的。你必须完全按照图中显示的那样放置它们,这样cgo才能知道要加载哪个头文件和库,在这种情况下:
// #cgo LDFLAGS: -L. -llibrary
// #include "library-bridge.h"
import "C"
链接到GitHub repo的完整示例。
您可能需要向Golang/CGo的LDFlags添加-lc++来识别对标准库的需求。
看来这是关于戈朗的一个早期问题。同时回答永不更新。在这三到四年间,有太多的新图书馆和博客文章问世了。下面是我觉得有用的几个链接。
SWIG和Go
使用SWIG调用Go中的c++代码
在比较语言方面,c++和Go
GoForCPPProgrammers
从go1.2+开始,cgo自动合并和编译c++代码:
http://golang.org/doc/go1.2#cgo_and_cpp
推荐文章
- 我可以列出所有的标准Go包吗?
- decltype(auto)的一些用途是什么?
- Shared_ptr转换为数组:应该使用它吗?
- Printf与std::字符串?
- 在Go中将float64转换为int类型
- 禁用复制构造函数
- 只接受特定类型的c++模板
- c#和Java中的泛型有什么不同?和模板在c++ ?
- c++ 11中的递归lambda函数
- 在c++中指针使用NULL或0(零)吗?
- 在c++中,如何将int值附加到字符串中?
- 就性能而言,使用std::memcpy()还是std::copy()更好?
- 为什么布尔值是1字节而不是1位?
- 四舍五入到一个数字的最接近倍数
- 人们如何在Go中管理身份验证?