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

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

Ответ 1

Обновление: мне удалось связать небольшой тестовый класс 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;
}

который я хочу использовать в Go. Я буду использовать интерфейс 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 [email protected] -c $(CGO_CFLAGS) $<
cfoo.o:cfoo.cpp
    g++ $(_CGO_CFLAGS_$(GOARCH)) -fPIC -O2 -o [email protected] -c $(CGO_CFLAGS) $<
CGO_LDFLAGS+=-lstdc++
$(elem)_foo.so: foo.cgo4.o foo.o cfoo.o
    gcc $(_CGO_CFLAGS_$(GOARCH)) $(_CGO_LDFLAGS_$(GOOS)) -o [email protected] $^ $(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

Ответ 2

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

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

Он поддерживает наследование и даже позволяет подклассифицировать класс С++ с помощью структуры Go, поэтому, когда переопределенные методы вызывают в коде С++, код Go запускается.

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

Ответ 3

Вы еще не можете понять, что я читал в FAQ:

Ссылки на программы Do Go с программами C/С++?

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

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

Ответ 5

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

SWIG и Go

Вызов кода С++ с Go With SWIG

При сравнении языков, С++ и Go

GoForCPPProgrammers

Ответ 6

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

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

#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 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++ 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" в программе go являются необязательными. Вы должны поместить их точно так, как показано, чтобы cgo знал, какой заголовок и библиотеку загружать, в этом случае:

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

Ссылка на репозиторий GitHub с полным примером.

Ответ 7

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

Ответ 8

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

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

Ответ 9

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

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

Ответ 10

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

Ответ 11

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

Ответ 12

Это может быть достигнуто с помощью команды cgo.

По сути "Если импорту" C "предшествует комментарий, этот комментарий, называемый преамбулой, используется в качестве заголовка при компиляции частей C пакета. Например: '
Источник:https://golang.org/cmd/cgo/

// #include <stdio.h>
// #include <errno.h>
import "C"