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 partir de go1.2 +, cgo automáticamente incorpora y compila el código C ++:
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"
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:
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 ".
Parece que es una de las primeras preguntas sobre Golang. Y a la vez respuestas para nunca actualizar. Durante estos tres o cuatro años, han salido muchas nuevas bibliotecas y publicaciones de blogs. Debajo están los pocos enlaces que sentí útil.
Llamar al código de C ++ desde GO con SWIG
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