GoでC ++を使用する方法


173

新しいGo言語でC ++コードを呼び出すにはどうすればよいですか?つまり、C ++クラスをラップしてGoで使用するにはどうすればよいですか?


1
テクニカルトークでは、SWIGは非常に簡潔に述べられており、「.. swigが完了するまで...」のように
StackedCrooked

1
@Matt:おそらく、彼は既存のC ++ライブラリをCまたはGoに移植せずに使用したいと考えています。同じものが欲しかった。
Graeme Perrow、2010

私はC ++ではなくC ++で利用できるまともなライブラリを1つ考えることはできません。あなたが何を考えているのか知りたいです。
マットジョイナー

13
@マット:1つの例はBoostライブラリで、他にも数千の便利なC ++ライブラリがあります。しかし、多分私はここでトロールに餌を与えているだけです...
フランク

@マット:私の場合、既存のクライアントライブラリへのGoインターフェイスを作成したかったのですが、ライブラリは主にC ++です。CやGoへの移植はオプションではありません。
Graeme Perrow、2009

回答:


154

更新:小さなテスト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

void*コンパイラーがFooのサイズを認識できるように、C構造体の代わりにa を使用しています)

実装は次のとおりです。

//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
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

1
これに注意してください。2つの言語間でメモリを送信すると、メモリがどうなるかわかりません。
スコットウェールズ

11
この例は、純粋なGoを書きたい理由を思い出させてくれます。C ++側がどれほど大きくて醜いか見てください。いや。
ジェフアレン

@ScottWalesこれをGithubまたは何かのリポジトリに置いた可能性はありますか?実用的な例を見てみたい
netpoetica '09 / 09/24

7
@Arne:回答は最善ではないため、反対票を投じないでください。回答が役に立たないため、回答に反対票を投じます。それが機能する限り、より良い解決策がある場合でも、この回答は役に立ちます。
Graeme Perrow、2014年

良いニュースです。Goがcppをコンパイルするので、makefileは必要ありません。unsafe.Pointerラッパーは機能しませんでした。私のためにコンパイルされたわずかな変更:play.golang.org/p/hKuKV51cRp go testはメイクファイルなしで動作するはずです
Drew

47

現在SWIGがこのための最良のソリューションであると思われます。

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

継承をサポートし、Go構造体でC ++クラスをサブクラス化することもできるため、オーバーライドされたメソッドがC ++コードで呼び出されると、Goコードが起動されます。

Go FAQのC ++に関するセクションが更新され、SWIGについて言及され、「Goはガベージコレクションされているため、少なくとも単純にそうするのは賢明ではありません」とは表示されなくなりました。


9
これを上げる方法があったらいいのに。他の答えは時代遅れです。Plus SWIGはswig.org/Doc3.0/Go.html
dragonx

34

私がFAQで読んだことからはまだまだできません:

GoプログラムはC / C ++プログラムとリンクしていますか?

Goコンパイラーの実装には、gc(6gプログラムとその仲間)とgccgoの2つがあります。Gcは異なる呼び出し規約とリンカーを使用するため、同じ規約を使用するCプログラムとのみリンクできます。そのようなCコンパイラはありますが、C ++コンパイラはありません。GccgoはGCCでコンパイルされたCまたはC ++プログラムと注意深くリンクできるGCCフロントエンドです。

cgoプログラムは、GoコードからCライブラリを安全に呼び出すための「外部関数インターフェース」のメカニズムを提供します。SWIGはこの機能をC ++ライブラリに拡張します。



13

スコットウェールズの回答に基づいて、次の例を作成しました。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.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;
}

(3)に実装されているAPI library-bridge.hを公​​開して使用できるようにするために必要なブリッジのコード。CC++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、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())
}

次の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"

完全な例を含むGitHubリポジトリへのリンク


ありがとう-これはとても役に立ちました!
Robert Cowham


3

gcc Goコンパイラ、gccgoを使用する場合のCとGoの相互運用性についての話があります。ただし、gccgoを使用する場合、相互運用性と実装されているGoの機能セットの両方に制限があります(たとえば、限定されたゴルーチン、ガベージコレクションなし)。


2
1.手動メモリ管理の機能がない言語を作成します。2。ガベージコレクションを削除しますか?これで頭を掻くのは私だけですか?
ジェルジAndrasek

2

ここは未知の領域を歩いています。ここでは、おそらくあなたは上に読んだ後そのような何かを行うことができ、Cのコードを呼び出すために行くの一例であるC ++名前の符号化及び規則、および試行錯誤の多くを呼び出します。

それでも試してみたい場合は、頑張ってください。


1

ここでの問題は、準拠した実装がクラスをコンパイル.cppファイルに入れる必要がないことです。コンパイラーがクラスの存在を最適化できる場合、それがなくてもプログラムが同じように動作する限り、出力実行可能ファイルから省略できます。

Cには、標準化されたバイナリインターフェイスがあります。したがって、関数がエクスポートされたことを知ることができます。しかし、C ++にはそのような標準はありません。


1

標準ライブラリの必要性を認識するために、Golang / CGoにを追加-lc++する必要がある場合がありLDFlagsます。


0

おかしいことに、この発表で取り上げられたより広範な問題の数。Dan Lykeは、彼のWebサイトFlutterbyで、新しい言語(およびその他の影響)をブートストラップする方法としてInterprocess Standardsを開発することについて、非常に楽しく思慮深いディスカッションをしました。


0

これは、コマンドcgoを使用して実現できます。

本質的には、「C」のインポートの直前にコメントがある場合、そのコメントはプリアンブルと呼ばれ、パッケージのC部分をコンパイルするときにヘッダーとして使用されます。たとえば、次のように」
ソース:https://golang.org/cmd/cgo/

// #include <stdio.h>
// #include <errno.h>
import "C"
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.