Как использовать C++ в Go?


новый Go язык, как я могу назвать код C++? Другими словами, как я могу обернуть свои классы C++ и использовать их в Go?

11 137

11 ответов:

обновление: мне удалось связать небольшой тестовый класс C++ с Go

если вы обернете код C++ с интерфейсом C, вы сможете вызвать свою библиотеку с помощью cgo (см. Пример gmp в $GOROOT/misc/cgo/gmp).

Я не уверен, что идея класса В C++ действительно выражается в Go, так как у него нет наследования.

вот пример:

у меня есть класс C++, определенный как:

// 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;
}

что я хотите использовать в ходу. Я буду использовать интерфейс C

// foo.h
#ifdef __cplusplus
extern "C" {
#endif
  typedef void* Foo;
  Foo FooInit(void);
  void FooFree(Foo);
  void FooBar(Foo);
#ifdef __cplusplus
}
#endif

(я использую void* вместо структуры C, чтобы компилятор знал размер Foo)

реализация:

//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();
}

со всем этим сделано, файл 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));
}

файл 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)

попробуйте проверить его с помощью:

// foo_test.go
package foo
import "testing"
func TestFoo(t *testing.T){
    foo := New();
    foo.Bar();
    foo.Free();
}

вам нужно будет установить общую библиотеку с помощью make install, а затем запустить make test. Ожидаемые результаты это:

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

кажется, что в настоящее время SWIG является лучшим решением для этого:

http://www.swig.org/Doc2.0/Go.html

Он поддерживает наследование и даже позволяет подкласс класса C++ с Go struct поэтому, когда переопределенные методы вызываются в коде C++, код Go запускается.

раздел О C++ в Go FAQ обновляется и теперь упоминает SWIG и больше не говорит"потому что Go-это сбор мусора, это будет неразумно, по крайней мере наивно".

вы еще не можете из того, что я читал в FAQ:

есть ли связь программ Go с программами C / C++?

есть две реализации компилятора Go, gc (программа 6g и друзья) и gccgo. Gc использует другое соглашение о вызовах и компоновщик и поэтому может быть связан только с программами C, использующими то же соглашение. Есть такой компилятор C, но компилятор C++. Поэтому его можно использовать для переднего плана ССАГПЗ, которые могут быть связаны с ССЗ-составитель C или C++ программы.

программа cgo предоставляет механизм для "внешнего интерфейса функций", чтобы обеспечить безопасный вызов библиотек C из кода Go. SWIG расширяет эту возможность до библиотек C++.

начиная с go1. 2+, cgo автоматически включает и компилирует код C++:

http://golang.org/doc/go1.2#cgo_and_cpp

выглядит это одним из первых задают вопрос о Golang . И в то же время ответы никогда не обновляются . За эти три-четыре года появилось слишком много новых библиотек и блогов . Ниже приведены несколько ссылок, что я чувствовал себя полезным .

глотни и иди

вызов кода C++ из Go With SWIG

при сравнении языков C++ и Go

GoForCPPProgrammers

я создал следующий пример на основе ответ Скотта Уэльса. Я тестировал его в macOS High Sierra 10.13.3 под управлением go версия go1.10 darwin/amd64.

(1) Код для library.hpp, API C++, который мы стремимся вызвать.

#pragma once
class Foo {
 public:
  Foo(int value);
  ~Foo();
  int value() const;    
 private:
  int m_value;
};

(2) Код library.cpp, реализация 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) Код library-bridge.h мост должен был выставить C API реализован в C++, так что go можно использовать оно.

#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 реализация моста.

#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) и, наконец, library.go, программа go, вызывающая 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())
}

используя следующий 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

я могу запустить пример программы следующим образом:

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

важно

комментарии выше import "C" на

там говорят о взаимодействие между C и Go при использовании компилятора GCC Go, gccgo. Однако существуют ограничения как на совместимость, так и на реализованный набор функций Go при использовании gccgo (например, ограниченные goroutines, без сбора мусора).

вы идете по неизведанной территории. здесь Это пример Go для вызова кода C, возможно, вы можете сделать что-то подобное после прочтения на C++ name mangling и соглашения о вызовах, и много проб и ошибок.

Если вы все еще хотите попробовать, удачи.

проблема здесь в том, что совместимая реализация не должна помещать ваши классы в компиляцию .файл cpp. Если компилятор может оптимизировать существование класса, пока программа ведет себя одинаково без него, то он может быть опущен из выходного исполняемого файла.

C имеет стандартизированный двоичный интерфейс. Поэтому вы сможете узнать, что ваши функции экспортируются. Но C++ не имеет такого стандарта за ним.

забавно, сколько более широких вопросов это объявление раскопало. У Дэна Лайка была очень интересная и вдумчивая дискуссия на его веб-сайте, Flutterby, о разработке Стандарты Процессов как способ начальной загрузки новых языков (и других разветвлений, но это тот, который здесь уместен).

возможно, вам придется добавить -lc++ до LDFlags для Golang / CGo, чтобы признать необходимость стандартной библиотеки.