如何在 Go 中使用 C++

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1713214/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 20:58:53  来源:igfitidea点击:

How to use C++ in Go

c++wrappergo

提问by Frank

In the new Golanguage, how do I call C++ code? In other words, how can I wrap my C++ classes and use them in Go?

在新的Go语言中,如何调用 C++ 代码?换句话说,我如何包装我的 C++ 类并在 Go 中使用它们?

采纳答案by Scott Wales

Update:I've succeeded in linking a small test C++ class with Go

更新:我已经成功地将一个小的测试 C++ 类与 Go 链接起来

If you wrap you C++ code with a C interface you should be able to call your library with cgo (see the example of gmp in $GOROOT/misc/cgo/gmp).

如果您使用 C 接口包装 C++ 代码,您应该能够使用 cgo 调用您的库(请参阅 中的 gmp 示例$GOROOT/misc/cgo/gmp)。

I'm not sure if the idea of a class in C++ is really expressible in Go, as it doesn't have inheritance.

我不确定 C++ 中的类的想法是否真的可以在 Go 中表达,因为它没有继承。

Here's an example:

下面是一个例子:

I have a C++ class defined as:

我有一个 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;
}

which I want to use in Go. I'll use the C interface

我想在 Go 中使用。我将使用 C 接口

// foo.h
#ifdef __cplusplus
extern "C" {
#endif
  typedef void* Foo;
  Foo FooInit(void);
  void FooFree(Foo);
  void FooBar(Foo);
#ifdef __cplusplus
}
#endif

(I use a void*instead of a C struct so the compiler knows the size of Foo)

(我使用 avoid*而不是 C 结构,因此编译器知道 Foo 的大小)

The implementation is:

实现是:

//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();
}

with all that done, the Go file is:

完成所有这些后,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));
}

The makefile I used to compile this was:

我用来编译这个的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)

Try testing it with:

尝试使用以下方法对其进行测试:

// foo_test.go
package foo
import "testing"
func TestFoo(t *testing.T){
    foo := New();
    foo.Bar();
    foo.Free();
}

You'll need to install the shared library with make install, then run make test. Expected output is:

您需要使用 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

回答by kolen

Seems that currently SWIG is best solution for this:

似乎目前 SWIG 是最好的解决方案:

http://www.swig.org/Doc2.0/Go.html

http://www.swig.org/Doc2.0/Go.html

It supports inheritance and even allows to subclass C++ class with Go struct so when overridden methods are called in C++ code, Go code is fired.

它支持继承,甚至允许使用 Go struct 对 C++ 类进行子类化,因此当在 C++ 代码中调用覆盖的方法时,会触发 Go 代码。

Section about C++ in Go FAQis updated and now mentions SWIG and no longer says "because Go is garbage-collected it will be unwise to do so, at least naively".

Go FAQ 中关于 C++ 的部分已更新,现在提到 SWIG 并且不再说“因为 Go 是垃圾收集的,所以这样做是不明智的,至少是天真”。

回答by Dirk Eddelbuettel

You can't quite yet from what I read in the FAQ:

您还不能完全从我在常见问题解答中读到的内容

Do Go programs link with C/C++ programs?

There are two Go compiler implementations, gc (the 6g program and friends) and gccgo. Gc uses a different calling convention and linker and can therefore only be linked with C programs using the same convention. There is such a C compiler but no C++ compiler. Gccgo is a GCC front-end that can, with care, be linked with GCC-compiled C or C++ programs.

The cgo program provides the mechanism for a “foreign function interface” to allow safe calling of C libraries from Go code. SWIG extends this capability to C++ libraries.

Go 程序是否与 C/C++ 程序链接?

有两个 Go 编译器实现,gc(6g 程序和朋友)和 gccgo。Gc 使用不同的调用约定和链接器,因此只能与使用相同约定的 C 程序链接。有这样的 C 编译器,但没有 C++ 编译器。Gccgo 是一个 GCC 前端,可以小心地与 GCC 编译的 C 或 C++ 程序链接。

cgo 程序提供了“外部函数接口”机制,允许从 Go 代码安全调用 C 库。SWIG 将此功能扩展到 C++ 库。

回答by Malcolm

As of go1.2+, cgo automatically incorporates and compiles C++ code:

从 go1.2+ 开始,cgo 会自动合并和编译 C++ 代码:

http://golang.org/doc/go1.2#cgo_and_cpp

http://golang.org/doc/go1.2#cgo_and_cpp

回答by Escualo

I've created the following example based on Scott Wales' answer. I've tested it in macOS High Sierra 10.13.3 running goversion go1.10 darwin/amd64.

我根据Scott Wales 的回答创建了以下示例。我已经在 macOS High Sierra 10.13.3 运行go版本中对其进行了测试go1.10 darwin/amd64

(1) Code for library.hpp, the C++ API we aim to call.

(1) 的代码library.hpp,我们打算调用的 C++ API。

#pragma once
class Foo {
 public:
  Foo(int value);
  ~Foo();
  int value() const;    
 private:
  int m_value;
};

(2) Code for library.cpp, the C++ implementation.

(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) Code for library-bridge.hthe bridge needed to expose a CAPI implemented in C++so that gocan use it.

(3)library-bridge.h桥的代码需要公开C实现的APIC++以便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) Code for library-bridge.cpp, the implementation of the bridge.

(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) Finally, library.go, the go program calling the C++ API.

(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())
}

Using the following Makefile

使用以下 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

I can run the example program as follows:

我可以按如下方式运行示例程序:

$ 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)

Important

重要的

The comments above import "C"in the goprogram are NOT OPTIONAL. You must put them exactly as shown so that cgoknows which header and library to load, in this case:

上述意见import "C"go程序是不可选的。您必须完全按照所示放置它们,以便cgo知道要加载哪个头文件和库,在这种情况下:

// #cgo LDFLAGS: -L. -llibrary
// #include "library-bridge.h"
import "C"

Link to GitHub repo with the full example.

使用完整示例链接到 GitHub 存储库

回答by Pravin Mishra

Looks it's one of the early asked question about Golang . And same time answers to never update . During these three to four years , too many new libraries and blog post has been out . Below are the few links what I felt useful .

看起来这是关于 Golang 的早期问题之一。同时回答从不更新。在这三到四年中,太多的新库和博客文章已经发布。下面是几个我觉得有用的链接。

SWIG and Go

SWIG 和 Go

Calling C++ Code From Go With SWIG

使用 SWIG 从 Go 调用 C++ 代码

On comparing languages, C++ and Go

比较语言,C++ 和 Go

GoForCPPProgrammers

GoForCPP程序员

回答by fbrereto

There's talk about interoperability between C and Gowhen using the gcc Go compiler, gccgo. There are limitations both to the interoperability and the implemented feature set of Go when using gccgo, however (e.g., limited goroutines, no garbage collection).

当使用 gcc Go 编译器 gccgo 时,讨论了C 和 Go 之间的互操作性。然而,在使用 gccgo 时,Go 的互操作性和实现的功能集都存在限制(例如,有限的 goroutine,没有垃圾收集)。

回答by Gy?rgy Andrasek

You're walking on uncharted territory here. Hereis the Go example for calling C code, perhaps you can do something like that after reading up on C++ name manglingand calling conventions, and lots of trial and error.

你在这里行走在未知的领域。是调用 C 代码的 Go 示例,也许您可​​以在阅读C++ 名称修改和调用约定以及大量试验和错误后做类似的事情。

If you still feel like trying it, good luck.

如果你仍然想尝试,祝你好运。

回答by Billy ONeal

The problem here is that a compliant implementation does not need to put your classes in a compile .cpp file. If the compiler can optimize out the existence of a class, so long as the program behaves the same way without it, then it can be omitted from the output executable.

这里的问题是兼容的实现不需要将您的类放在编译 .cpp 文件中。如果编译器可以优化一个类的存在,只要程序在没有它的情况下以相同的方式运行,那么它就可以从输出可执行文件中省略。

C has a standardized binary interface. Therefore you'll be able to know that your functions are exported. But C++ has no such standard behind it.

C 有一个标准化的二进制接口。因此,您将能够知道您的函数已导出。但是 C++ 背后没有这样的标准。

回答by ggobieski

You might need to add -lc++to the LDFlagsfor Golang/CGo to recognize the need for the standard library.

您可能需要添加-lc++LDFlagsfor Golang/CGo 以识别对标准库的需求。