Wie rufe ich in der neuen Go- Sprache C ++ - Code auf? Mit anderen Worten, wie kann ich meine C ++ - Klassen umbrechen und in Go verwenden?
Wie rufe ich in der neuen Go- Sprache C ++ - Code auf? Mit anderen Worten, wie kann ich meine C ++ - Klassen umbrechen und in Go verwenden?
Antworten:
Update: Es ist mir gelungen, eine kleine Test-C ++ - Klasse mit Go zu verknüpfen
Wenn Sie Ihren C ++ - Code mit einer C-Schnittstelle umschließen, sollten Sie in der Lage sein, Ihre Bibliothek mit cgo aufzurufen (siehe das Beispiel von gmp in $GOROOT/misc/cgo/gmp
).
Ich bin mir nicht sicher, ob die Idee einer Klasse in C ++ in Go wirklich ausgedrückt werden kann, da sie keine Vererbung hat.
Hier ist ein Beispiel:
Ich habe eine C ++ - Klasse definiert als:
// 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;
}
was ich in Go verwenden möchte. Ich werde die C-Schnittstelle verwenden
// foo.h
#ifdef __cplusplus
extern "C" {
#endif
typedef void* Foo;
Foo FooInit(void);
void FooFree(Foo);
void FooBar(Foo);
#ifdef __cplusplus
}
#endif
(Ich verwende eine void*
anstelle einer C-Struktur, damit der Compiler die Größe von Foo kennt.)
Die Implementierung ist:
//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();
}
Nach all dem lautet die Go-Datei:
// 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));
}
Das Makefile, mit dem ich das kompiliert habe, war:
// 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)
Versuchen Sie es mit:
// foo_test.go
package foo
import "testing"
func TestFoo(t *testing.T){
foo := New();
foo.Bar();
foo.Free();
}
Sie müssen die gemeinsam genutzte Bibliothek mit make install installieren und dann make test ausführen. Die erwartete Ausgabe ist:
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
go test
sollte ohne das Makefile funktionieren
Derzeit scheint SWIG die beste Lösung dafür zu sein:
http://www.swig.org/Doc2.0/Go.html
Es unterstützt die Vererbung und ermöglicht sogar die Unterklasse von C ++ - Klassen mit Go-Struktur. Wenn überschriebene Methoden in C ++ - Code aufgerufen werden, wird Go-Code ausgelöst.
Der Abschnitt über C ++ in den Go-FAQ wird aktualisiert und erwähnt nun SWIG und sagt nicht mehr " weil Go Müll gesammelt hat, ist es unklug, dies zumindest naiv zu tun ".
Sie können noch nicht ganz von dem, was ich in den FAQ gelesen habe :
Verknüpfen Go-Programme mit C / C ++ - Programmen?
Es gibt zwei Go-Compiler-Implementierungen, gc (das 6g-Programm und Freunde) und gccgo. Gc verwendet eine andere Aufrufkonvention und einen anderen Linker und kann daher nur mit C-Programmen verknüpft werden, die dieselbe Konvention verwenden. Es gibt einen solchen C-Compiler, aber keinen C ++ - Compiler. Gccgo ist ein GCC-Frontend, das mit Sorgfalt mit GCC-kompilierten C- oder C ++ - Programmen verknüpft werden kann.
Das Programm cgo bietet den Mechanismus für eine „Fremdfunktionsschnittstelle“, um das sichere Aufrufen von C-Bibliotheken aus Go-Code zu ermöglichen. SWIG erweitert diese Funktion auf C ++ - Bibliotheken.
Ab go1.2 + integriert und kompiliert cgo automatisch C ++ - Code:
Ich habe das folgende Beispiel basierend auf der Antwort von Scott Wales erstellt . Ich habe es in der laufenden go
Version von macOS High Sierra 10.13.3 getestet go1.10 darwin/amd64
.
(1) Code für library.hpp
die C ++ - API, die wir aufrufen möchten.
#pragma once
class Foo {
public:
Foo(int value);
~Foo();
int value() const;
private:
int m_value;
};
(2) Code für library.cpp
die C ++ - Implementierung.
#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 für library-bridge.h
die Bridge, der erforderlich ist, um eine C
implementierte API verfügbar zu machen, C++
damit diese go
verwendet werden kann.
#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 für library-bridge.cpp
die Umsetzung der Brücke.
#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) Schließlich library.go
das go-Programm, das die C ++ - API aufruft.
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())
}
Verwenden Sie das folgende 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
Ich kann das Beispielprogramm wie folgt ausführen:
$ 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)
Wichtig
Die obigen Kommentare import "C"
im go
Programm sind NICHT OPTIONAL . Sie müssen sie genau wie gezeigt platzieren, damit Sie cgo
wissen, welcher Header und welche Bibliothek geladen werden sollen. In diesem Fall:
// #cgo LDFLAGS: -L. -llibrary
// #include "library-bridge.h"
import "C"
Sieht so aus, als wäre es eine der früh gestellten Fragen zu Golang. Und gleichzeitig Antworten nie zu aktualisieren. In diesen drei bis vier Jahren sind zu viele neue Bibliotheken und Blogposts erschienen. Unten sind die wenigen Links aufgeführt, die ich für nützlich hielt.
Aufrufen von C ++ - Code von Anfang an mit SWIG
Bei Verwendung des gcc Go-Compilers gccgo wird über die Interoperabilität zwischen C und Go gesprochen. Bei der Verwendung von gccgo gibt es jedoch Einschränkungen sowohl für die Interoperabilität als auch für den implementierten Funktionsumfang von Go (z. B. eingeschränkte Goroutinen, keine Speicherbereinigung).
Sie betreten hier Neuland. Hier ist das Go-Beispiel für das Aufrufen von C-Code. Vielleicht können Sie so etwas tun, nachdem Sie sich mit den Konventionen zum Verwalten und Aufrufen von C ++ - Namen und vielen Versuchen und Irrtümern befasst haben.
Wenn Sie es immer noch versuchen möchten, viel Glück.
Das Problem hierbei ist, dass eine kompatible Implementierung Ihre Klassen nicht in eine kompilierte CPP-Datei einfügen muss. Wenn der Compiler die Existenz einer Klasse optimieren kann, solange sich das Programm ohne sie genauso verhält, kann sie in der ausführbaren Ausgabedatei weggelassen werden.
C hat eine standardisierte binäre Schnittstelle. Daher können Sie wissen, dass Ihre Funktionen exportiert werden. Aber C ++ hat keinen solchen Standard dahinter.
Komisch, wie viele umfassendere Themen diese Ankündigung ausgebaggert hat. Dan Lyke hatte auf seiner Website Flutterby eine sehr unterhaltsame und nachdenkliche Diskussion über die Entwicklung von Interprozessstandards , um neue Sprachen zu booten (und andere Konsequenzen, aber das ist diejenige, die hier von Bedeutung ist).
Dies kann mit dem Befehl cgo erreicht werden.
Im Wesentlichen 'Wenn dem Import von "C" unmittelbar ein Kommentar vorausgeht, wird dieser Kommentar, der als Präambel bezeichnet wird, als Header beim Kompilieren der C-Teile des Pakets verwendet. Zum Beispiel: '
Quelle: https://golang.org/cmd/cgo/
// #include <stdio.h>
// #include <errno.h>
import "C"