新しい Go 言語では、C++コードをどのように呼び出すのですか?つまり、C++クラスをラップしてGoで使用するにはどうすればよいですか?
更新:小さなテスト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;
}
goで使用したいです。 Cインターフェースを使用します
// foo.h
#ifdef __cplusplus
extern "C" {
#endif
typedef void* Foo;
Foo FooInit(void);
void FooFree(Foo);
void FooBar(Foo);
#ifdef __cplusplus
}
#endif
(C構造体の代わりにvoid*
を使用するため、コンパイラは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
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
現在、SWIGはこれに最適なソリューションであると思われます。
http://www.swig.org/Doc2.0/Go.html
継承をサポートし、Go構造体でC++クラスをサブクラス化することもできるため、C++コードでオーバーライドされたメソッドが呼び出されると、Goコードが起動されます。
Go FAQのC++に関するセクション が更新され、SWIGについて言及され、「」とは言われなくなりました。 )。
あなたはまだ私が読んだものからまだできません FAQで :
GoプログラムはC/C++プログラムとリンクしますか?
Goコンパイラには、gc(6gプログラムとその仲間)とgccgoの2つの実装があります。 Gcは異なる呼び出し規約とリンカーを使用するため、同じ規約を使用するCプログラムとのみリンクできます。そのようなCコンパイラはありますが、C++コンパイラはありません。 GccgoはGCCフロントエンドであり、注意してGCCでコンパイルされたCまたはC++プログラムとリンクできます。
Cgoプログラムは、GoコードからのCライブラリの安全な呼び出しを可能にする「外部関数インターフェイス」のメカニズムを提供します。 SWIGは、この機能をC++ライブラリに拡張します。
Go1.2 +以降、cgoはC++コードを自動的に組み込み、コンパイルします。
Golangについての初期の質問の1つであるようです。そして、同じ時間は決して更新しないと答えます。この3年から4年の間に、あまりに多くの新しいライブラリとブログ投稿が出てきました。以下は私が役に立つと感じたいくつかのリンクです。
Scott Wales 'answer に基づいて次の例を作成しました。 go
バージョンgo1.10 darwin/AMD64
を実行しているmacOS High Sierra 10.13.3でテストしました。
(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
が使用できるように、C++
に実装されたgo
APIを公開するために必要なブリッジです。
#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)
重要
go
プログラムのimport "C"
上のコメントはNOT OPTIONALです。この場合、cgo
がどのヘッダーとライブラリをロードするかを知るために、示されているとおりに正確に配置する必要があります。
// #cgo LDFLAGS: -L. -llibrary
// #include "library-bridge.h"
import "C"
CとGoの相互運用性 についての話があります。gccGoコンパイラgccgoを使用する場合です。ただし、gccgoを使用する場合、相互運用性とGoの実装された機能セットの両方に制限があります(例:ゴルーチンの制限、ガベージコレクションなし)。
ここでは未知の領域を歩いています。 ここ は、Cコードを呼び出すためのGoの例です。おそらく、 C++名マングリング と呼び出し規約、および多くの試行錯誤を読んだ後、そのようなことができます。
それでも試してみたいと思うなら、幸運を祈ります。
ここでの問題は、準拠する実装では、クラスをコンパイル.cppファイルに入れる必要がないことです。コンパイラーがクラスの存在を最適化できる場合、プログラムがクラスなしで同じように動作する限り、出力実行可能ファイルから省略できます。
Cには標準化されたバイナリインターフェイスがあります。したがって、関数がエクスポートされたことを知ることができます。しかし、C++にはそのような標準がありません。
おかしなことに、この発表がどれほど広範な問題を引き起こしたのか。 Dan Lykeは、彼のWebサイトFlutterbyで Interprocess Standards を新しい言語(およびその他の影響)をブートストラップする方法として開発することについて、非常に面白くて思慮深い議論を行いました。
標準ライブラリの必要性を認識するために、Golang/CGoのLDFlags
に-lc++
を追加する必要があるかもしれません。