Nella nuova lingua Go , come posso chiamare il codice C ++? In altre parole, come posso avvolgere le mie classi C ++ e usarle in Go?
Nella nuova lingua Go , come posso chiamare il codice C ++? In altre parole, come posso avvolgere le mie classi C ++ e usarle in Go?
Risposte:
Aggiornamento: sono riuscito a collegare una piccola classe C ++ di prova con Go
Se avvolgi il tuo codice C ++ con un'interfaccia C, dovresti essere in grado di chiamare la tua libreria con cgo (vedi l'esempio di gmp in $GOROOT/misc/cgo/gmp
).
Non sono sicuro che l'idea di una classe in C ++ sia davvero espressibile in Go, in quanto non ha eredità.
Ecco un esempio:
Ho una classe C ++ definita come:
// 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;
}
che voglio usare in Go. Userò l'interfaccia C.
// foo.h
#ifdef __cplusplus
extern "C" {
#endif
typedef void* Foo;
Foo FooInit(void);
void FooFree(Foo);
void FooBar(Foo);
#ifdef __cplusplus
}
#endif
(Uso una struttura void*
anziché una C in modo che il compilatore conosca le dimensioni di Foo)
L'implementazione è:
//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();
}
fatto tutto ciò, il file 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));
}
Il makefile che ho usato per compilare questo era:
// 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)
Prova a provarlo con:
// foo_test.go
package foo
import "testing"
func TestFoo(t *testing.T){
foo := New();
foo.Bar();
foo.Free();
}
Dovrai installare la libreria condivisa con make install, quindi esegui make test. L'output previsto è:
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
dovrebbe funzionare senza il makefile
Sembra che attualmente SWIG sia la soluzione migliore per questo:
http://www.swig.org/Doc2.0/Go.html
Supporta l'ereditarietà e consente persino di sottoclassare la classe C ++ con Go struct, quindi quando i metodi sovrascritti vengono chiamati nel codice C ++, il codice Go viene attivato.
La sezione relativa al C ++ nelle Domande frequenti su Go viene aggiornata e ora menziona SWIG e non dice più " poiché Go è stato spazzato via, non è saggio farlo, almeno ingenuamente ".
Non puoi ancora abbastanza da quello che ho letto nelle FAQ :
I programmi Go si collegano ai programmi C / C ++?
Esistono due implementazioni del compilatore Go, gc (il programma 6g e amici) e gccgo. Gc utilizza una convenzione di chiamata e un linker diversi e pertanto può essere collegato solo ai programmi C utilizzando la stessa convenzione. Esiste un tale compilatore C ma nessun compilatore C ++. Gccgo è un front-end GCC che può, con cura, essere collegato con i programmi C o C ++ compilati da GCC.
Il programma cgo fornisce il meccanismo per una "interfaccia di funzione esterna" per consentire la chiamata sicura delle librerie C dal codice Go. SWIG estende questa capacità alle librerie C ++.
A partire da go1.2 +, cgo incorpora e compila automaticamente il codice C ++:
Ho creato il seguente esempio basato sulla risposta di Scott Wales . L'ho provato in macOS High Sierra 10.13.3 go
versione corrente go1.10 darwin/amd64
.
(1) Codice per library.hpp
, l'API C ++ che intendiamo chiamare.
#pragma once
class Foo {
public:
Foo(int value);
~Foo();
int value() const;
private:
int m_value;
};
(2) Codice per library.cpp
l'implementazione 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) Codice per library-bridge.h
il bridge necessario per esporre C
un'API implementata in C++
modo che go
possa usarla.
#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) Codice per library-bridge.cpp
, l'implementazione del ponte.
#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) Infine, library.go
il programma go chiama 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())
}
Utilizzando il seguente 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
Posso eseguire il programma di esempio come segue:
$ 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)
Importante
I commenti sopra riportati import "C"
nel go
programma NON SONO FACOLTATIVI . Devi metterli esattamente come mostrato in modo che cgo
sappia quale intestazione e libreria caricare, in questo caso:
// #cgo LDFLAGS: -L. -llibrary
// #include "library-bridge.h"
import "C"
Sembra che sia una delle prime domande su Golang. E allo stesso tempo risponde a non aggiornare mai. Durante questi tre o quattro anni, sono state pubblicate troppe nuove biblioteche e post di blog. Di seguito sono riportati i pochi link che mi sono sentito utili.
Chiamata del codice C ++ da Vai con SWIG
Si parla di interoperabilità tra C e Go quando si utilizza il compilatore gcc Go, gccgo. Ci sono limiti sia all'interoperabilità che al set di funzionalità implementate di Go quando si utilizza gccgo (ad es. Goroutine limitate, nessuna raccolta dei rifiuti).
Il problema qui è che un'implementazione conforme non ha bisogno di mettere le tue classi in un file .cpp compilato. Se il compilatore può ottimizzare l'esistenza di una classe, purché il programma si comporti allo stesso modo senza di essa, allora può essere omesso dall'eseguibile di output.
C ha un'interfaccia binaria standardizzata. Pertanto, sarai in grado di sapere che le tue funzioni vengono esportate. Ma C ++ non ha tali standard dietro.
Divertente quante questioni più ampie questo annuncio è stato risolto. Dan Lyke ha avuto una discussione molto divertente e ponderata sul suo sito web, Flutterby, sullo sviluppo degli Standard Interprocesso come un modo per avviare nuove lingue (e altre ramificazioni, ma è quello che è germano qui).
Questo può essere ottenuto usando il comando cgo.
In sostanza 'Se l'importazione di "C" è immediatamente preceduta da un commento, quel commento, chiamato preambolo, viene usato come intestazione durante la compilazione delle parti C del pacchetto. Ad esempio: '
fonte: https://golang.org/cmd/cgo/
// #include <stdio.h>
// #include <errno.h>
import "C"