W nowym języku Go , jak wywołać kod C ++? Innymi słowy, jak mogę opakować moje klasy C ++ i używać ich w Go?
W nowym języku Go , jak wywołać kod C ++? Innymi słowy, jak mogę opakować moje klasy C ++ i używać ich w Go?
Odpowiedzi:
Aktualizacja: Udało mi się połączyć małą testową klasę C ++ z Go
Jeśli otoczysz kod C ++ interfejsem C, powinieneś móc wywołać swoją bibliotekę za pomocą cgo (zobacz przykład gmp w $GOROOT/misc/cgo/gmp
).
Nie jestem pewien, czy idea klasy w C ++ jest naprawdę wyrażalna w Go, ponieważ nie ma dziedziczenia.
Oto przykład:
Mam klasę C ++ zdefiniowaną jako:
// 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;
}
którego chcę używać w Go. Użyję interfejsu C.
// foo.h
#ifdef __cplusplus
extern "C" {
#endif
typedef void* Foo;
Foo FooInit(void);
void FooFree(Foo);
void FooBar(Foo);
#ifdef __cplusplus
}
#endif
(Używam struktury void*
zamiast C, więc kompilator zna rozmiar Foo)
Wdrożenie to:
//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();
}
po wykonaniu tego wszystkiego plik Go to:
// 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));
}
Plik makefile, którego użyłem do skompilowania tego, to:
// 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)
Spróbuj to przetestować z:
// foo_test.go
package foo
import "testing"
func TestFoo(t *testing.T){
foo := New();
foo.Bar();
foo.Free();
}
Musisz zainstalować udostępnioną bibliotekę za pomocą polecenia make install, a następnie uruchomić make test. Oczekiwany wynik to:
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
powinno działać bez pliku makefile
Wygląda na to, że obecnie SWIG jest najlepszym rozwiązaniem na to:
http://www.swig.org/Doc2.0/Go.html
Obsługuje dziedziczenie, a nawet pozwala na podklasę klasy C ++ za pomocą struktury Go, więc gdy nadpisane metody są wywoływane w kodzie C ++, kod Go jest uruchamiany.
Sekcja o C ++ w Go FAQ została zaktualizowana i teraz wspomina o SWIG i nie mówi już „ ponieważ Go jest zbierające śmieci, byłoby to nierozsądne, przynajmniej naiwnie ”.
Nie możesz jeszcze z tego, co przeczytałem w FAQ :
Czy programy Go łączą się z programami C / C ++?
Istnieją dwie implementacje kompilatora Go, gc (program 6g i przyjaciele) i gccgo. Gc używa innej konwencji wywoływania i konsolidatora i dlatego może być łączony tylko z programami w C używającymi tej samej konwencji. Jest taki kompilator C, ale nie ma kompilatora C ++. Gccgo to front-end GCC, który można ostrożnie łączyć z programami C lub C ++ skompilowanymi przez GCC.
Program cgo zapewnia mechanizm „interfejsu funkcji obcych”, który umożliwia bezpieczne wywoływanie bibliotek C z kodu Go. SWIG rozszerza tę możliwość na biblioteki C ++.
Począwszy od go1.2 +, cgo automatycznie włącza i kompiluje kod C ++:
Poniższy przykład stworzyłem na podstawie odpowiedzi Scotta Walesa . Przetestowałem to w uruchomionej go
wersji macOS High Sierra 10.13.3 go1.10 darwin/amd64
.
(1) Kod library.hpp
interfejsu API C ++, który chcemy wywołać.
#pragma once
class Foo {
public:
Foo(int value);
~Foo();
int value() const;
private:
int m_value;
};
(2) Kod library.cpp
, implementacja 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) Kod library-bridge.h
mostu potrzebny do udostępnienia C
interfejsu API zaimplementowanego w programie C++
, aby go
móc go używać.
#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) Kod library-bridge.cpp
implementacji mostu.
#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) Na koniec library.go
program go wywołujący 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())
}
Używając następującego pliku 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
Przykładowy program mogę uruchomić w następujący sposób:
$ 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)
Ważny
Komentarze powyżej import "C"
w go
programie NIE SĄ OPCJONALNE . Musisz umieścić je dokładnie tak, jak pokazano, aby cgo
wiedzieć, który nagłówek i bibliotekę załadować, w tym przypadku:
// #cgo LDFLAGS: -L. -llibrary
// #include "library-bridge.h"
import "C"
Wygląda na to, że to jedno z wczesnych pytań o Golang. Jednocześnie odpowiada, aby nigdy nie aktualizować. W ciągu tych trzech do czterech lat opublikowano zbyt wiele nowych bibliotek i postów na blogu. Poniżej znajduje się kilka linków, które uznałem za przydatne.
Wywołanie kodu C ++ z Go z SWIG
Podczas korzystania z kompilatora gcc Go, gccgo, mówi się o interoperacyjności między C i Go . Istnieją jednak ograniczenia zarówno co do współdziałania, jak i zaimplementowanego zestawu funkcji Go podczas korzystania z gccgo (np. Ograniczone gorutyny, brak czyszczenia pamięci).
Kroczysz tu po niezbadanym terenie. Oto przykład Go do wywoływania kodu w C, być może możesz zrobić coś takiego po przeczytaniu o zniekształcaniu nazw w C ++ i konwencjach wywoływania oraz wielu próbach i błędach.
Jeśli nadal masz ochotę spróbować, powodzenia.
Problem polega na tym, że zgodna implementacja nie musi umieszczać twoich klas w skompilowanym pliku .cpp. Jeśli kompilator może zoptymalizować istnienie klasy, o ile program zachowuje się bez niej w ten sam sposób, można go pominąć w wyjściowym pliku wykonywalnym.
C ma ustandaryzowany interfejs binarny. Dzięki temu będziesz mógł wiedzieć, że Twoje funkcje są eksportowane. Ale C ++ nie ma za sobą takiego standardu.
Zabawne, jak wiele szerszych kwestii pogłębiło to ogłoszenie. Dan Lyke przeprowadził bardzo zabawną i przemyślaną dyskusję na swojej stronie internetowej Flutterby na temat rozwijania standardów międzyprocesowych jako sposobu na ładowanie nowych języków (i innych konsekwencji, ale to jest tutaj istotne).
Można to osiągnąć za pomocą polecenia cgo.
W istocie „Jeśli import„ C ”jest bezpośrednio poprzedzony komentarzem, komentarz ten, zwany preambułą, jest używany jako nagłówek podczas kompilowania części C pakietu. Na przykład: „
źródło: https://golang.org/cmd/cgo/
// #include <stdio.h>
// #include <errno.h>
import "C"