Yeni Git dilinde, C ++ kodunu nasıl arayabilirim? Başka bir deyişle, C ++ sınıflarımı nasıl sarabilir ve Go'da kullanabilirim?
Yeni Git dilinde, C ++ kodunu nasıl arayabilirim? Başka bir deyişle, C ++ sınıflarımı nasıl sarabilir ve Go'da kullanabilirim?
Yanıtlar:
Güncelleme: Küçük bir test C ++ sınıfını Go ile bağlamayı başardım
C ++ kodunu bir C arayüzü ile sararsanız, kütüphanenizi cgo ile arayabilirsiniz (gmp in örneğine bakınız $GOROOT/misc/cgo/gmp
).
Kalıtım olmadığı için C ++ 'da bir sınıf fikrinin Go'da gerçekten ifade edilebilir olup olmadığından emin değilim.
İşte bir örnek:
Ben tanımlanmış bir C ++ sınıfı var:
// 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;
}
Go'da kullanmak istiyorum. C arayüzünü kullanacağım
// foo.h
#ifdef __cplusplus
extern "C" {
#endif
typedef void* Foo;
Foo FooInit(void);
void FooFree(Foo);
void FooBar(Foo);
#ifdef __cplusplus
}
#endif
( void*
Derleyici bir Foo boyutunu bilmek böylece bir C yapı yerine kullanın )
Uygulama:
//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();
}
tüm bunlar bittiğinde, Go dosyası:
// 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));
}
Bunu derlemek için kullandığım makefile:
// 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)
Şunlarla test etmeyi deneyin:
// foo_test.go
package foo
import "testing"
func TestFoo(t *testing.T){
foo := New();
foo.Bar();
foo.Free();
}
Paylaşılan kitaplığı make install ile yüklemeniz ve ardından make testini çalıştırmanız gerekir. Beklenen çıktı:
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
, tefekkür olmadan çalışmalıdır
Şu anda SWIG'nin bunun için en iyi çözüm olduğu anlaşılıyor:
http://www.swig.org/Doc2.0/Go.html
Devralınmayı destekler ve hatta C ++ kodunda geçersiz kılınan yöntemler çağrıldığında, Go kodu tetiklenir.
Go SSS'de C ++ ile ilgili bölüm güncellenir ve şimdi SWIG'den bahseder ve artık " Go çöp toplandığından en azından naif bir şekilde bunu yapmak akıllıca olmaz" der .
SSS bölümünde okuduklarımdan henüz emin olamazsınız :
Go programları C / C ++ programlarına bağlanır mı?
İki Go derleyici uygulaması vardır: gc (6g programı ve arkadaşları) ve gccgo. Gc farklı bir çağrı kuralı ve bağlayıcı kullanır ve bu nedenle yalnızca aynı kuralı kullanan C programlarına bağlanabilir. Böyle bir C derleyicisi var ama C ++ derleyicisi yok. Gccgo, dikkatle GCC-derlenmiş C veya C ++ programlarına bağlanabilen bir GCC ön ucudur.
Cgo programı, C kütüphanelerinin Go kodundan güvenli çağrılmasını sağlayan bir “yabancı fonksiyon arayüzü” mekanizması sağlar. SWIG bu özelliği C ++ kitaplıklarına genişletir.
Go1.2 + itibariyle, cgo otomatik olarak C ++ kodunu birleştirir ve derler:
Scott Wales'in cevabına dayanarak aşağıdaki örneği oluşturdum . MacOS High Sierra 10.13.3 çalışan go
sürümünde test ettim go1.10 darwin/amd64
.
(1) Kod library.hpp
, aramayı amaçladığımız C ++ API'sı.
#pragma once
class Foo {
public:
Foo(int value);
~Foo();
int value() const;
private:
int m_value;
};
(2) library.cpp
C ++ uygulaması için kod .
#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 için library-bridge.h
köprü bir açığa gerekli C
uygulanan API C++
böylece go
kullanabilirsiniz.
#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) library-bridge.cpp
Köprünün uygulanmasını kodlayın .
#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) Son olarak, library.go
C ++ API'sini çağıran go programı.
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())
}
Aşağıdaki Makefile kullanma
liblibrary.so: library.cpp library-bridge.cpp
clang++ -o liblibrary.so library.cpp library-bridge.cpp \
-std=c++17 -O3 -Wall -Wextra -fPIC -shared
Örnek programı aşağıdaki gibi çalıştırabilirim:
$ 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)
Önemli
Yukarıdaki yorumlar import "C"
içinde go
programa olan İSTEĞE DEĞİL . Bunları tam olarak gösterildiği gibi koymanız gerekir, böylece cgo
hangi başlık ve kitaplığın yükleneceğini bilir, bu durumda:
// #cgo LDFLAGS: -L. -llibrary
// #include "library-bridge.h"
import "C"
Görünüşe göre Golang hakkında sorulan sorulardan biri. Ve aynı zamanda asla güncellemek için cevaplar. Bu üç ila dört yıl boyunca çok fazla yeni kütüphane ve blog yazısı çıktı. Yararlı hissettiğim birkaç bağlantı aşağıda.
SWIG ile Go'dan C ++ Kodunu Çağırma
Gcc Go derleyicisini, gccgo kullanırken C ve Go arasında birlikte çalışabilirlik hakkında konuşma var . Bununla birlikte, gccgo kullanılırken hem birlikte çalışabilirlik hem de Go'nun uygulanan özellik kümesinde sınırlamalar vardır (örn. Sınırlı goroutines, çöp toplama yok).
Burada keşfedilmemiş topraklarda yürüyorsun. İşte C kodunu çağırmak için Go örneği, belki de C ++ adı yönetme ve çağırma kurallarını okuduktan sonra böyle bir şey yapabilirsiniz ve çok sayıda deneme yanılma.
Hala denemek istiyorsan, iyi şanslar.
Buradaki sorun, uyumlu bir uygulamanın sınıflarınızı derleme .cpp dosyasına koymasına gerek olmamasıdır. Derleyici, program onsuz aynı şekilde davrandığı sürece bir sınıfın varlığını optimize edebilirse, çıktı yürütülebilir dosyasından çıkarılabilir.
C'nin standart bir ikili arayüzü vardır. Bu nedenle, işlevlerinizin dışa aktarıldığını bileceksiniz. Ancak C ++ 'ın arkasında böyle bir standart yoktur.
Bu duyurunun kaç tane daha geniş sayıya sahip olduğu komik. Dan Lyke, Flutterby adlı web sitesinde, yeni dilleri (ve diğer sonuçları) yeniden başlatmanın bir yolu olarak İnterprocess Standartlarını geliştirme konusunda çok eğlenceli ve düşünceli bir tartışma yaptı , ancak buradaki almanca).
Bu cgo komutu kullanılarak elde edilebilir.
Özünde '"C" nin içe aktarılmasından hemen önce bir yorum gelirse, önsöz adı verilen bu yorum, paketin C kısımlarını derlerken başlık olarak kullanılır. Örneğin: '
kaynak: https://golang.org/cmd/cgo/
// #include <stdio.h>
// #include <errno.h>
import "C"