如何在Go中使用C++?

Fra*_*ank 158 c++ wrapper go

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

Sco*_*les 142

更新:我已成功将一个小型测试C++类与Go相关联

如果用C接口包装C++代码,你应该可以用cgo调用你的库(参见$ GOROOT/misc/cgo/gmp中的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;
}
Run Code Online (Sandbox Code Playgroud)

我想在Go中使用它.我将使用C接口

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

(我使用的是一个$GOROOT/misc/cgo/gmp而不是一个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();
}
Run Code Online (Sandbox Code Playgroud)

完成所有这些后,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));
}
Run Code Online (Sandbox Code Playgroud)

我用来编译它的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)
Run Code Online (Sandbox Code Playgroud)

尝试测试它:

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

您需要使用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
Run Code Online (Sandbox Code Playgroud)

  • +1好工作.你可以帮我换Qt吗?:P (87认同)
  • 我得说,这个例子提醒我为什么我要写纯Go.看看C++方面有多大和多丑.伊克. (11认同)
  • @Arne:你没有给出答案,因为它不是最好的.你拒绝回答是因为它没有帮助.只要它有效,即使有更好的解决方案,这个答案仍然有用. (5认同)

kol*_*len 42

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

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

它支持继承,甚至允许使用Go结构子类化C++类,因此当在C++代码中调用重写方法时,会触发Go代码.

Go中关于C++的部分更新了,现在提到SWIG并且不再说" 因为Go是垃圾收集的,这样做是不明智的,至少是天真的 ".

  • 我希望有一种方法可以解决这个问题.其他答案已过时.另外,SWIG已经版本化http://www.swig.org/Doc3.0/Go.html (7认同)
  • @dragonx ...几年后,现在有了版本 4。:-) (2认同)

Dir*_*tel 33

你还不能从我在FAQ中读到的内容中得到:

Do Go程序与C/C++程序有关吗?

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

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

  • 现在最新的SWIG支持Go:http://www.swig.org/Doc2.0/Go.html (8认同)
  • go1.2 +中的cgo支持C++,请参阅http://golang.org/doc/go1.2#cgo_and_cpp (5认同)

Mal*_*olm 25

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

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


Pra*_*hra 11

看起来这是关于Golang的早期问题之一.同时答案永远不会更新.在这三到四年的时间里,有太多的新图书馆和博客文章被淘汰出局.以下是我觉得有用的几个链接.

SWIG和Go

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

比较语言,C++和Go

GoForCPPProgrammers


Esc*_*alo 8

我根据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;
};
Run Code Online (Sandbox Code Playgroud)

(2)library.cppC++实现的代码.

#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;
}
Run Code Online (Sandbox Code Playgroud)

(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
Run Code Online (Sandbox Code Playgroud)

(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();
}
Run Code Online (Sandbox Code Playgroud)

(5)最后,library.gogo程序调用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())
}
Run Code Online (Sandbox Code Playgroud)

使用以下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
Run Code Online (Sandbox Code Playgroud)

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

$ 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)
Run Code Online (Sandbox Code Playgroud)

重要

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

// #cgo LDFLAGS: -L. -llibrary
// #include "library-bridge.h"
import "C"
Run Code Online (Sandbox Code Playgroud)

使用完整示例链接到GitHub repo.