Как использовать C++ в Go?
новый Go язык, как я могу назвать код C++? Другими словами, как я могу обернуть свои классы C++ и использовать их в Go?
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++.
выглядит это одним из первых задают вопрос о Golang . И в то же время ответы никогда не обновляются . За эти три-четыре года появилось слишком много новых библиотек и блогов . Ниже приведены несколько ссылок, что я чувствовал себя полезным .
я создал следующий пример на основе ответ Скотта Уэльса. Я тестировал его в 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, о разработке Стандарты Процессов как способ начальной загрузки новых языков (и других разветвлений, но это тот, который здесь уместен).