No novo idioma Go , como chamo de código C ++? Em outras palavras, como posso agrupar minhas classes C ++ e usá-las no Go?
No novo idioma Go , como chamo de código C ++? Em outras palavras, como posso agrupar minhas classes C ++ e usá-las no Go?
Respostas:
Atualizar: Consegui vincular uma pequena classe C ++ de teste ao Go
Se você agrupar seu código C ++ com uma interface C, poderá chamar sua biblioteca com cgo (veja o exemplo de gmp em $GOROOT/misc/cgo/gmp
).
Não tenho certeza se a idéia de uma classe em C ++ é realmente expressável no Go, pois ela não tem herança.
Aqui está um exemplo:
Eu tenho uma classe 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 eu quero usar no Go. Vou usar a interface C
// foo.h
#ifdef __cplusplus
extern "C" {
#endif
typedef void* Foo;
Foo FooInit(void);
void FooFree(Foo);
void FooBar(Foo);
#ifdef __cplusplus
}
#endif
(Eu uso uma void*
estrutura C em vez de C para que o compilador saiba o tamanho de Foo)
A implementação é:
//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();
}
com tudo isso feito, o arquivo 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));
}
O makefile que eu usei para compilar isso foi:
// 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)
Tente testá-lo com:
// foo_test.go
package foo
import "testing"
func TestFoo(t *testing.T){
foo := New();
foo.Bar();
foo.Free();
}
Você precisará instalar a biblioteca compartilhada com make install e, em seguida, execute make test. A produção esperada é:
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
deve funcionar sem o makefile
Parece que atualmente o SWIG é a melhor solução para isso:
http://www.swig.org/Doc2.0/Go.html
Ele suporta herança e até permite subclassificar a classe C ++ com a estrutura Go, portanto, quando métodos substituídos são chamados no código C ++, o código Go é acionado.
A seção sobre perguntas frequentes sobre C ++ no Go é atualizada e agora menciona SWIG e não diz mais " porque o Go é coletado no lixo, não é aconselhável fazê-lo, pelo menos ingênuo ".
Ainda não é possível pelo que li nas Perguntas frequentes :
Os programas Go são vinculados aos programas C / C ++?
Existem duas implementações do compilador Go, gc (o programa 6g e amigos) e gccgo. O Gc usa uma convenção de chamada e vinculador diferentes e, portanto, só pode ser vinculado a programas C usando a mesma convenção. Existe um compilador C, mas nenhum compilador C ++. O Gccgo é um front-end do GCC que pode, com cuidado, ser vinculado a programas C ou C ++ compilados pelo GCC.
O programa cgo fornece o mecanismo para uma "interface de função externa" para permitir a chamada segura de bibliotecas C a partir do código Go. O SWIG estende esse recurso às bibliotecas C ++.
A partir do go1.2 +, o cgo incorpora e compila automaticamente o código C ++:
Eu criei o exemplo a seguir com base na resposta de Scott Wales . Eu testei no macOS High Sierra 10.13.3 go
versão em execuçãogo1.10 darwin/amd64
.
(1) Código para library.hpp
a API C ++ que pretendemos chamar.
#pragma once
class Foo {
public:
Foo(int value);
~Foo();
int value() const;
private:
int m_value;
};
(2) Código para library.cpp
a implementação 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 library-bridge.h
da ponte necessário para expor umaC
API implementada C++
para que ela go
possa ser usada.
#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
a implementação da 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) Finalmente, library.go
o programa go chamando a 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())
}
Usando o seguinte 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
Eu posso executar o programa de exemplo da seguinte maneira:
$ 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
Os comentários acima import "C"
no go
programa NÃO SÃO OPCIONAIS . Você deve colocá-los exatamente como mostrado para que cgo
saiba qual cabeçalho e biblioteca carregar, neste caso:
// #cgo LDFLAGS: -L. -llibrary
// #include "library-bridge.h"
import "C"
Parece que é uma das perguntas mais antigas sobre Golang. E respostas ao mesmo tempo para nunca atualizar. Durante esses três a quatro anos, muitas novas bibliotecas e publicações no blog foram lançadas. Abaixo estão os poucos links que eu achei úteis.
Chamando código C ++ a partir do SWIG
Fala-se de interoperabilidade entre C e Go ao usar o compilador gcc Go, gccgo. Entretanto, existem limitações para a interoperabilidade e o conjunto de recursos implementados do Go ao usar o gccgo (por exemplo, goroutines limitadas, sem coleta de lixo).
Você está andando em território desconhecido aqui. Aqui está o exemplo do Go para chamar o código C, talvez você possa fazer algo assim depois de ler as convenções de manipulação e chamada de nomes C ++ e várias tentativas e erros.
Se você ainda quiser experimentá-lo, boa sorte.
O problema aqui é que uma implementação compatível não precisa colocar suas classes em um arquivo .cpp de compilação. Se o compilador puder otimizar a existência de uma classe, desde que o programa se comporte da mesma maneira sem ela, ele poderá ser omitido do executável de saída.
C tem uma interface binária padronizada. Portanto, você poderá saber que suas funções são exportadas. Mas o C ++ não tem esse padrão por trás disso.
Engraçado quantas edições mais amplas este anúncio foi divulgado. Dan Lyke teve uma discussão muito divertida e atenciosa em seu site, Flutterby, sobre o desenvolvimento de Interprocess Standards como uma maneira de inicializar novas linguagens (e outras ramificações, mas essa é a mais pertinente aqui).
Isso pode ser alcançado usando o comando cgo.
Essencialmente 'Se a importação de "C" for imediatamente precedida por um comentário, esse comentário, chamado de preâmbulo, será usado como cabeçalho ao compilar as partes C do pacote. Por exemplo: '
source: https://golang.org/cmd/cgo/
// #include <stdio.h>
// #include <errno.h>
import "C"