Domanda

Nel nuovo Vai lingua , Come chiamare in codice C ++? In altre parole, come posso avvolgere le mie classi C ++ e utilizzarli in Go?

È stato utile?

Soluzione

Aggiornamento: sono riuscito a collegare un piccolo test classe C ++ con Go

Se si avvolgerà codice C ++ con un'interfaccia C si dovrebbe essere in grado di chiamare la libreria con CGO (si veda l'esempio del GMP $GOROOT/misc/cgo/gmp).

Non sono sicuro se l'idea di una classe in C ++ è davvero esprimibili in Go, in quanto non ha l'ereditarietà.

Ecco un esempio:

ho una classe C ++ definito 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

(io uso un void* invece di una struct C in modo che il compilatore conosce la dimensione dei 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();
}

con tutto quello che fatto, 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 ho usato per compilare questo è stato:

// 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 testare con:

// foo_test.go
package foo
import "testing"
func TestFoo(t *testing.T){
    foo := New();
    foo.Bar();
    foo.Free();
}

È necessario installare la libreria condivisa con il make install, quindi eseguire make test. risultato atteso è:

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

Altri suggerimenti

Sembra che attualmente SWIG è la soluzione migliore per questo:

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

supporta l'ereditarietà e permette anche di creare una sottoclasse classe C ++ con Go struct così quando Metodi sostituiti vengono chiamati in codice C ++, Vai codice viene licenziato.

sezione su C ++ in Go FAQ viene aggiornato e ora menzioni SWIG e non dice più " perché Go è garbage collection sarà saggio farlo, almeno ingenuamente ".

Non si può ancora del tutto da quello che leggo nelle FAQ :

  

Do not Go programmi dei collegamenti con / C ++ programmi in C?

     

Ci sono due implementazioni compilatore Go, GC (il programma di 6g e amici) e gccgo. Gc utilizza un diverso convenzione di chiamata e linker e può quindi essere collegato solo con programmi C utilizzando la stessa convenzione. C'è una tale compilatore C, ma nessun compilatore C ++. Gccgo è un front-end GCC che può, con cura, essere collegati con i programmi C ++ C GCC-compilato o.

     

Il programma CGO fornisce il meccanismo per una “funzione di interfaccia straniero” per consentire la chiamata sicura di C librerie da codice Go. SWIG estende questa capacità di librerie C ++.

A partire dal go1.2 +, CGO incorpora e compila automaticamente C ++ Codice:

http://golang.org/doc/go1.2#cgo_and_cpp

Sembra che sia uno dei primi ha chiesto domanda su Golang. E stesse risposte di tempo di non aggiornare. Durante questi tre o quattro anni, troppe nuove librerie e blog post è stato fuori. Di seguito sono riportati i pochi collegamenti quello che sentivo utile.

SWIG and Go

Calling C ++ Codice Da Go With SWIG

sulle lingue a confronto, C ++ and Go

GoForCPPProgrammers

Ho creato l'esempio seguente in base a Scott Galles risposta . Ho provato in MacOS High Sierra 10.13.3 in esecuzione la versione go go1.10 darwin/amd64.

(1) Codice per library.hpp, l'API C ++ ci proponiamo di 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 ponte necessario per esporre un'API C implementato in modo che C++ go può utilizzare.

#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, la realizzazione 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 chiamando 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())
}

Con 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 nel seguente modo:

$ 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

Le osservazioni di cui sopra import "C" nel programma go sono non facoltativo . È necessario metterli esattamente come mostrato in modo che cgo sappia quale intestazione e la biblioteca per caricare, in questo caso:

// #cgo LDFLAGS: -L. -llibrary
// #include "library-bridge.h"
import "C"

Link GitHub pronti contro termine con l'esempio completo .

Si parla sui interoperabilità tra C e Go quando si utilizza il gcc Go compilatore, gccgo. Ci sono limitazioni sia per l'interoperabilità e il set di funzionalità implementata di Go quando si utilizza gccgo, tuttavia (ad esempio, goroutines limitati, senza la raccolta dei rifiuti).

Stai camminando su un territorio inesplorato qui. Qui è l'esempio Go per chiamare il codice C, forse si può fare una cosa del genere dopo aver letto su C ++ nome mangling e convenzioni di chiamata, e un sacco di tentativi ed errori.

Se ancora voglia di provare, buona fortuna.

Il problema qui è che un'implementazione compatibile non ha bisogno di mettere le classi in un file cpp compilazione. Se il compilatore in grado di ottimizzare l'esistenza di una classe, a condizione che il programma si comporta allo stesso modo senza di essa, allora può essere omesso dalla eseguibile uscita.

C è dotato di un'interfaccia standardizzata binario. Quindi sarete in grado di sapere che le funzioni vengono esportate. Ma C ++ non ha tale standard dietro di esso.

Divertente quanti più ampio le questioni presente bando ha ripescato. Dan Lyke ha avuto una discussione molto divertente e riflessivo sul suo sito web, Flutterby, di sviluppare Interprocess Standards come un modo di bootstrap nuovi linguaggi (e altre ramificazioni, ma questo è quello che è pertinente qui).

Potrebbe essere necessario aggiungere -lc++ al LDFlags per Golang / CGO a riconoscere la necessità per la libreria standard.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top