Go'da C ++ nasıl kullanılır


173

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?


1
Teknoloji konuşmasında SWIG çok kısaca bahsedildi, "
..göbeği bitirene kadar

1
@Matt: Mevcut bir C ++ kütüphanesini C veya Go'ya taşımak zorunda kalmadan kullanmak istiyor. Aynı şeyi istedim.
Graeme Perrow

C ++ için mevcut olmayan tek bir iyi kütüphane düşünemiyorum. Aklınızda neler olduğunu bilmek isterim.
Matt Joiner

13
@Matt: Bir örnek Boost kitaplığıdır ve binlerce başka yararlı C ++ kitaplığı vardır. Ama belki sadece burada bir trol besliyorum ...
Frank

@Matt: benim durumumda, mevcut istemci kütüphanemize bir Go arayüzü yapmak istedim ama kütüphane esas olarak C ++. C veya Go'ya taşımak basit bir seçenek değil.
Graeme Perrow

Yanıtlar:


154

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

1
Bu konuda dikkatli olun, iki dil arasında gönderirseniz belleğe ne olabileceği hakkında hiçbir fikrim yok.
Scott Wales

11
Söylemeliyim ki, bu örnek bana neden saf Go yazmak istediğimi hatırlatıyor. C ++ tarafının ne kadar büyük ve daha çirkin olduğuna bakın. Ick.
Jeff Allen

@ScottWith'i Github ya da başka bir şeyle ilgili bir depoya koyma şansınız var mı? Çalışan bir örnek görmek isterdim
Netpoetica

7
@Arne: Bir cevabı küçümsemiyorsunuz çünkü bu en iyisi değil. Bir cevabı küçümsersiniz, çünkü bu yararlı değildir. Çalıştığı sürece, daha iyi çözümler olsa bile bu cevap hala yararlıdır.
Graeme Perrow

İyi haber, Go artık cpp derleyecek, böylece makefile artık gerekli değil. Güvensiz.Pointer sarmalayıcıları benim için çalışmadı. Benim için derlenmiş küçük bir değişiklik: play.golang.org/p/hKuKV51cRp go test , tefekkür olmadan çalışmalıdır
Drew

47

Ş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 .


9
Keşke bunu çarpmanın bir yolu olsaydı. Diğer cevaplar güncel değil. Artı SWIG, swig.org/Doc3.0/Go.html
dragonx

34

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.



13

Scott Wales'in cevabına dayanarak aşağıdaki örneği oluşturdum . MacOS High Sierra 10.13.3 çalışan gosü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.cppC ++ 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.hköprü bir açığa gerekli Cuygulanan API C++böylece gokullanabilirsiniz.

#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.cppKö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.goC ++ 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 goprograma olan İSTEĞE DEĞİL . Bunları tam olarak gösterildiği gibi koymanız gerekir, böylece cgohangi başlık ve kitaplığın yükleneceğini bilir, bu durumda:

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

Tam örnekle GitHub repo bağlantısı .


Teşekkür ederim - bu çok yardımcı oldu!
Robert Cowham


3

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


2
1. Manuel bellek yönetimi için herhangi bir olanağı olmayan bir dil oluşturun, 2. Çöp toplama kaldırılsın mı? Bu konuda başımı kaşıyan tek kişi ben miyim?
György Andrasek

2

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.


1

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.


1

Sen eklemeniz gerekebilir -lc++için LDFlagsGolang / CGO standart kütüphanesinde ihtiyacını tanıması.


0

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


0

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"
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.