在新的Go语言中,如何调用C ++代码?换句话说,如何包装我的C ++类并在Go中使用它们?
在新的Go语言中,如何调用C ++代码?换句话说,如何包装我的C ++类并在Go中使用它们?
Answers:
更新:我已经成功地将一个小型测试C ++类与Go链接在一起
如果您使用C接口包装C ++代码,则应该可以使用cgo调用您的库(请参阅中的gmp示例$GOROOT/misc/cgo/gmp
)。
我不确定C ++中的类概念是否真的可以在Go中表达,因为它没有继承。
这是一个例子:
我有一个C ++类定义为:
// foo.hpp
class cxxFoo {
public:
int a;
cxxFoo(int _a):a(_a){};
~cxxFoo(){};
void Bar();
};
// foo.cpp
#include <iostream>
#include "foo.hpp"
void
cxxFoo::Bar(void){
std::cout<<this->a<<std::endl;
}
我想在Go中使用。我将使用C接口
// foo.h
#ifdef __cplusplus
extern "C" {
#endif
typedef void* Foo;
Foo FooInit(void);
void FooFree(Foo);
void FooBar(Foo);
#ifdef __cplusplus
}
#endif
(我使用a void*
而不是C结构,因此编译器知道Foo的大小)
实现是:
//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();
}
完成所有操作后,Go文件为:
// foo.go
package foo
// #include "foo.h"
import "C"
import "unsafe"
type GoFoo struct {
foo C.Foo;
}
func New()(GoFoo){
var ret GoFoo;
ret.foo = C.FooInit();
return ret;
}
func (f GoFoo)Free(){
C.FooFree(unsafe.Pointer(f.foo));
}
func (f GoFoo)Bar(){
C.FooBar(unsafe.Pointer(f.foo));
}
我用来编译的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)
尝试使用以下方法进行测试:
// foo_test.go
package foo
import "testing"
func TestFoo(t *testing.T){
foo := New();
foo.Bar();
foo.Free();
}
您需要使用make install安装共享库,然后运行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
go test
应该在没有makefile的情况下可以工作
似乎目前SWIG是最佳解决方案:
http://www.swig.org/Doc2.0/Go.html
它支持继承,甚至允许使用Go struct子类化C ++类,因此,当在C ++代码中调用重写的方法时,将触发Go代码。
Go常见问题解答中有关C ++的部分进行了更新,现在提到了SWIG,不再说“ 因为Go已被垃圾回收,这样做是不明智的,至少是天真的。”
从go1.2 +开始,cgo自动合并并编译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
实现的API,C++
以便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
是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)
重要
上述意见import "C"
的go
程序是不可选的。您必须完全按照所示放置它们,以便cgo
知道要加载的标头和库,在这种情况下:
// #cgo LDFLAGS: -L. -llibrary
// #include "library-bridge.h"
import "C"
看来这是有关Golang的最早提出的问题之一。同时答案永远不会更新。在这三到四年中,出现了太多的新图书馆和博客文章。以下是我觉得有用的一些链接。
关于使用gcc Go编译器gccgo时C和Go之间的互操作性。但是,使用gccgo时,Go的互操作性和已实现的功能集都有限制(例如,有限的goroutine,无垃圾收集)。
这可以使用命令cgo来实现。
本质上,如果在导入“ C”之前紧跟注释,则在编译程序包的C部分时,该注释(称为前导)将用作标头。例如:'
来源:https://golang.org/cmd/cgo/
// #include <stdio.h>
// #include <errno.h>
import "C"