代码之家  ›  专栏  ›  技术社区  ›  Frank

如何在C++中使用GO?

  •  141
  • Frank  · 技术社区  · 15 年前

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

    11 回复  |  直到 6 年前
        1
  •  125
  •   Scott Wales    8 年前

    更新: 我已经成功地把一个小的测试C++类与GO连接起来了。

    如果用C接口包装C++代码,则应该能够用CGO调用库(参见GOOOT/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;
    }
    

    我想用在围棋上。我将使用C接口

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

    (我用 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
    
        2
  •  36
  •   kolen    12 年前

    目前看来,Swig是最好的解决方案:

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

    它支持继承,甚至允许用GO结构来对C++类进行子类化,所以当在C++代码中调用重写方法时,会启动GO代码。

    Section about C++ in Go FAQ 更新了,现在提到swig,不再说 因为Go是垃圾收集,所以这样做是不明智的,至少是幼稚的 “。

        3
  •  31
  •   Community holdenweb    11 年前

    你还不能完全从我读到的 in the FAQ :

    用C/C++程序连接程序吗?

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

    CGO程序提供了一个__外部函数接口__的机制,允许从Go代码安全调用C库。SWIG将此能力扩展到C++库。

        4
  •  22
  •   Malcolm    10 年前

    至于GOD1.+,CGO自动合并并编译C++代码:

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

        5
  •  10
  •   Pravin Mishra    10 年前

    看来这是关于歌朗的早期问题之一。同时回答永不更新。在这三到四年中,有太多的新图书馆和博客文章被删除了。以下是我觉得有用的几个链接。

    SWIG and Go

    Calling C++ Code From Go With SWIG

    On comparing languages, C++ and Go

    GoForCPPProgrammers

        6
  •  7
  •   Escualo    6 年前

    我创建的以下示例基于 Scott Wales' answer . 我在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++ 以便 可以使用它。

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

    使用以下生成文件

    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" 程序是 不可选 . 你必须把它们完全按图示放好 cgo 知道要加载哪个头和库,在这种情况下:

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

    Link to GitHub repo with the full example .

        7
  •  3
  •   fbrereto    15 年前

    有人在谈论 interoperability between C and Go 当使用gcc go编译器时,gcc go。但是,在使用GCCGO时,对互操作性和已实现的Go功能集都有限制(例如,有限的Goroutines,没有垃圾收集)。

        8
  •  2
  •   György Andrasek    15 年前

    你走在未知的领域。 Here 是调用C代码的好例子,也许在阅读完 C++ name mangling 还有召唤约定,还有很多尝试和错误。

    如果你还想试试,祝你好运。

        9
  •  1
  •   Billy ONeal IS4    15 年前

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

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

        10
  •  1
  •   Don Wakefield    15 年前

    有趣的是,这一声明引发了多少更广泛的问题。丹·莱克在他的网站flutrby上进行了一次非常有趣和深思熟虑的关于发展的讨论。 Interprocess Standards 作为一种引导新语言(和其他分支)的方法,但这就是这里的德语。

        11
  •  1
  •   jrbedard    7 年前

    您可能需要添加 -lc++ LDFlags 让Golang/CGO认识到对标准库的需求。