Comment utiliser C++ dans Go?
11 réponses
mise à Jour: j'ai réussi à lier à un petit test de la classe C++ avec Go
si vous enveloppez votre code C++ avec une interface C, vous devriez être en mesure d'appeler votre bibliothèque avec cgo (voir l'exemple de gmp dans $GOROOT/misc/cgo/gmp).
Je ne suis pas sûr que l'idée d'une classe en C++ soit vraiment expressible dans Go, car elle n'a pas d'héritage.
voici un exemple:
j'ai un c++ classe définie comme:
// 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;
}
que je veux utiliser dans Go. Je vais utiliser l'interface C
// foo.h
#ifdef __cplusplus
extern "C" {
#endif
typedef void* Foo;
Foo FooInit(void);
void FooFree(Foo);
void FooBar(Foo);
#ifdef __cplusplus
}
#endif
(j'utilise un void*
au lieu d'une structure C pour que le compilateur connaisse la taille de Foo)
La mise en œuvre est:
//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();
}
avec tout ce qui est fait, le fichier Go est:
// 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));
}
le makefile que j'ai utilisé pour compiler ceci était:
// 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)
Essayez de le tester avec:
// foo_test.go
package foo
import "testing"
func TestFoo(t *testing.T){
foo := New();
foo.Bar();
foo.Free();
}
vous aurez besoin d'installer la bibliothèque partagée avec make install, puis lancer make test. Résultats escomptés:
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
semble que SWIG est actuellement la meilleure solution pour cela:
http://www.swig.org/Doc2.0/Go.html
il supporte l'héritage et permet même de sous-Classe C++ Classe avec GO struct donc quand les méthodes dépassées sont appelées en code C++, le code Go est déclenché.
section À propos de C++ dans Go FAQ est mis à jour et mentionne maintenant SWIG et ne dit plus " parce que Go est ramassage des ordures il ne sera pas sage de le faire, au moins naïvement ".
vous ne pouvez pas encore de ce que je lis dans la FAQ :
Faire Passer des programmes de lien avec les programmes C/C++?
il y a deux implémentations de compilateurs de Go, gc (le programme 6g et les amis) et gccgo. Gc utilise une convention d'appel et un linker différents et ne peut donc être relié qu'à des programmes C utilisant la même convention. Il existe un tel compilateur C mais pas de compilateur C++. Gccgo est un front-end GCC qui peut, avec soin, être lié avec des programmes C ou C++ compilés par GCC.
le programme cgo fournit le mécanisme pour une" interface de fonction étrangère " pour permettre l'appel sûr des bibliothèques C à partir du code Go. SWIG étend cette capacité aux bibliothèques C++.
dès go1.2+, cgo incorpore et compile automatiquement le code C++:
semble être l'une des premières questions posées sur Golang . Et le même temps répond à ne jamais mettre à jour . Au cours de ces trois à quatre années , trop de nouvelles bibliothèques et de billets de blog ont été publiés . Voici les quelques liens que j'ai senti utile .
Appeler du Code C++ à Partir de Aller Avec SWIG
j'ai créé l'exemple suivant basé sur réponse de Scott Wales . Je l'ai testé dans macOS High Sierra 10.13.3 en lançant go
version go1.10 darwin/amd64
.
(1) Code pour library.hpp
, L'API C++ que nous voulons appeler.
#pragma once
class Foo {
public:
Foo(int value);
~Foo();
int value() const;
private:
int m_value;
};
(2) Code pour library.cpp
, l'implémentation 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) Code pour library-bridge.h
le pont nécessaire pour exposer un C
API implémentée dans C++
pour que go
puisse l'utiliser.
#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 pour library-bridge.cpp
, la mise en œuvre du pont.
#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) Enfin, library.go
, le programme go appelant L'API C++.
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())
}
utilisant le Makefile suivant
liblibrary.so: library.cpp library-bridge.cpp
clang++ -o liblibrary.so library.cpp library-bridge.cpp \
-std=c++17 -O3 -Wall -Wextra -fPIC -shared
je peux exécuter le programme d'exemple comme suit:
$ 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)
Important
Les commentaires ci-dessus import "C"
dans le go
programme PAS une OPTION . Vous devez les mettre exactement comme indiqué pour que cgo
sache quel en-tête et quelle bibliothèque charger, dans ce cas:
// #cgo LDFLAGS: -L. -llibrary
// #include "library-bridge.h"
import "C"
Il y a en parler l'interopérabilité entre C et lors de l'utilisation de la ccg Aller compilateur, gccgo. Il y a toutefois des limites à l'interopérabilité et à L'ensemble des fonctionnalités mises en œuvre de Go lors de l'utilisation de gccgo (p. ex. goroutines limitées, pas de collecte des ordures).
vous marchez en territoire inconnu ici. ici est L'exemple de Go pour appeler le code C, peut-être que vous pouvez faire quelque chose comme ça après avoir lu sur nom c++ mangeant et appelant conventions, et beaucoup d'essai et d'erreur.
si vous avez encore envie d'essayer, bonne chance.
le problème ici est qu'une implémentation conforme n'a pas besoin de mettre vos classes dans une compilation .fichier cpp. Si le compilateur peut optimiser l'existence d'une classe, tant que le programme se comporte de la même façon sans elle, alors il peut être omis de la sortie de l'exécutable.
C a une interface binaire standardisée. Par conséquent, vous serez en mesure de savoir que vos fonctions sont exportées. Mais C++ n'a pas de telle norme derrière lui.
Drôle combien de questions plus larges cette annonce a dragué. Dan Lyke a eu une discussion très divertissante et réfléchie sur son site Web, Flutterby, à propos du développement de Interprocess Standards comme un moyen de bootstrapping de nouvelles langues (et d'autres ramifications, mais c'est celui qui est pertinent ici).
vous pourriez avoir besoin d'ajouter -lc++
au LDFlags
pour Golang/CGo pour reconnaître le besoin de la bibliothèque standard.