versiones tipos sirve que para ejemplos definicion datos caracteristicas c++ wrapper go

sirve - tipos de datos en c++



¿Cómo usar C++ en Go? (11)

En el nuevo lenguaje Go , ¿cómo puedo llamar al código C ++? En otras palabras, ¿cómo puedo ajustar mis clases de C ++ y usarlas en Go?



Aún no puede leer lo que leí en las Preguntas frecuentes :

¿Los programas Do Go se vinculan con los programas C / C ++?

Hay dos implementaciones de compilador Go, gc (el programa 6g y amigos) y gccgo. Gc usa una convención de llamadas y un enlazador diferentes y, por lo tanto, solo se puede vincular con los programas C que utilizan la misma convención. Existe un compilador de C pero no un compilador de C ++. Gccgo es un front-end de GCC que, con cuidado, puede vincularse con los programas C o C ++ compilados por GCC.

El programa cgo proporciona el mecanismo para una "interfaz de función extranjera" para permitir la llamada segura de librerías C desde el código Go. SWIG extiende esta capacidad a las bibliotecas C ++.


Creé el siguiente ejemplo basado en la respuesta de Scott Wales . Lo he probado en macOS High Sierra 10.13.3 corriendo la versión go1.10 darwin/amd64 .

(1) Código para library.hpp , la API de C ++ que intentamos llamar.

#pragma once class Foo { public: Foo(int value); ~Foo(); int value() const; private: int m_value; };

(2) Código para library.cpp , la implementación de 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) Código para library-bridge.h el puente necesitaba exponer una API C implementada en C++ para que go pueda 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) Código para library-bridge.cpp , la implementación del puente.

#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) Finalmente, library.go , el programa go llama a la API de 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()) }

Usando el siguiente 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

Puedo ejecutar el programa de ejemplo de la siguiente manera:

$ 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

Los comentarios anteriores import "C" en el programa go NO SON OPCIONALES . Debe ponerlos exactamente como se muestra para que cgo sepa qué encabezado y biblioteca cargar, en este caso:

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

Enlace al repositorio de GitHub con el ejemplo completo .


El problema aquí es que una implementación compatible no necesita poner sus clases en un archivo .cpp de compilación. Si el compilador puede optimizar la existencia de una clase, siempre que el programa se comporte de la misma manera sin ella, entonces puede omitirse del ejecutable de salida.

C tiene una interfaz binaria estandarizada. Por lo tanto, podrá saber que sus funciones se exportan. Pero C ++ no tiene ese estándar detrás de esto.


Es curioso cuántos temas más amplios ha dragado este anuncio. Dan Lyke tuvo una discusión muy entretenida y reflexiva en su sitio web, Flutterby, sobre el desarrollo de estándares de interproceso como una forma de iniciar nuevos lenguajes (y otras ramificaciones, pero esa es la que está relacionada aquí).


Es posible que necesite agregar -lc++ a LDFlags para que Golang / CGo reconozca la necesidad de la biblioteca estándar.


Estás caminando por un territorio inexplorado aquí. Here está el ejemplo de Go para llamar al código C, quizás pueda hacer algo así después de leer las convenciones de llamada y manipulación de nombres C ++ , y muchas pruebas y errores.

Si aún quieres probarlo, buena suerte.


Parece que actualmente SWIG es la mejor solución para esto:

swig.org/Doc2.0/Go.html

Es compatible con la herencia e incluso permite subclasificar la clase C ++ con Go struct para que cuando se invoquen métodos anulados en el código C ++, se active el código Go.

La sección acerca de C ++ in Go FAQ se actualiza y ahora menciona SWIG y ya no dice " porque Go es basura recolectada será imprudente hacerlo, al menos ingenuamente ".



Se habla de interoperabilidad entre C y Go cuando se usa el compilador Go de gcc, gccgo. Sin embargo, existen limitaciones para la interoperabilidad y el conjunto de características implementado de Go cuando se usa gccgo (por ejemplo, goroutines limitados, sin recolección de basura).


Actualización: Logré vincular una pequeña prueba de la clase C ++ con Go

Si envuelve su código C ++ con una interfaz C, debería poder llamar a su biblioteca con cgo (vea el ejemplo de gmp en $ GOROOT / misc / cgo / gmp).

No estoy seguro si la idea de una clase en C ++ es realmente expresable en Go, ya que no tiene herencia.

Aquí hay un ejemplo:

Tengo una clase de C ++ definida como:

// 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 quiero usar en Go. Usaré la interfaz C

// foo.h #ifdef __cplusplus extern "C" { #endif typedef void* Foo; Foo FooInit(void); void FooFree(Foo); void FooBar(Foo); #ifdef __cplusplus } #endif

(Uso un void* lugar de una estructura C para que el compilador conozca el tamaño de Foo)

La implementación es:

//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 todo eso hecho, el archivo Go es:

// 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)); }

El archivo MAKE que utilicé para compilar esto fue:

// 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)

Pruebe probarlo con:

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

Deberá instalar la biblioteca compartida con make install, luego ejecutar make test. El resultado esperado es:

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