В новом языке Go , как я могу назвать код C ++? Другими словами, как я могу обернуть свои классы C ++ и использовать их в Go?
В новом языке Go , как я могу назвать код C ++? Другими словами, как я могу обернуть свои классы C ++ и использовать их в Go?
Ответы:
Обновление: мне удалось связать небольшой тестовый класс C ++ с Go
Если вы закроете свой код C ++ интерфейсом C, вы сможете вызывать вашу библиотеку с помощью cgo (см. Пример gmp in $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 $@ -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
go test
должна работать без make-файла
Похоже, что в настоящее время SWIG является лучшим решением для этого:
http://www.swig.org/Doc2.0/Go.html
Он поддерживает наследование и даже позволяет создавать подклассы класса C ++ со структурой Go, поэтому при вызове переопределенных методов в коде C ++ код Go запускается.
Раздел о C ++ в Go FAQ обновлен, и теперь в нем упоминается SWIG и больше не говорится, что « поскольку Go является сборщиком мусора, это будет неразумно, по крайней мере наивно ».
Вы не можете еще совсем из того, что я прочитал в FAQ :
Связаны ли программы Go с программами C / C ++?
Существует две реализации компилятора Go: gc (программа 6g и друзья) и gccgo. Gc использует другое соглашение о вызовах и компоновщик и поэтому может быть связано только с программами на Си, использующими то же соглашение. Существует такой компилятор C, но нет компилятора C ++. Gccgo - это интерфейс GCC, который может быть осторожно связан с программами C или C ++, скомпилированными GCC.
Программа cgo предоставляет механизм для «интерфейса внешних функций», позволяющий безопасно вызывать библиотеки C из кода Go. SWIG расширяет эту возможность для библиотек C ++.
Начиная с go1.2 +, cgo автоматически включает и компилирует код C ++:
Я создал следующий пример на основе ответа Скотта Уэльса . Я протестировал его в 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'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"
в go
программе НЕ ДОПОЛНИТЕЛЬНЫЕ . Вы должны поместить их точно так, как показано, чтобы они cgo
знали, какой заголовок и библиотеку загружать, в этом случае:
// #cgo LDFLAGS: -L. -llibrary
// #include "library-bridge.h"
import "C"
Похоже, это один из самых ранних вопросов о Голанге. И в то же время ответы никогда не обновлять. За эти три-четыре года появилось слишком много новых библиотек и постов в блогах. Ниже приведены несколько ссылок, которые мне показались полезными.
Говорят о совместимости между C и Go при использовании компилятора gcc Go, gccgo. Однако при использовании gccgo существуют ограничения как на функциональную совместимость, так и на реализованный набор функций Go (например, ограниченные процедуры, сборка мусора отсутствует).
Ты идешь по неизведанной территории. Вот пример Go для вызова кода на C, возможно, вы можете сделать что-то подобное после прочтения правил сортировки имен C ++ и соглашений о вызовах, а также множества проб и ошибок.
Если вам все еще хочется попробовать, удачи.
Проблема здесь в том, что совместимая реализация не должна помещать ваши классы в файл компиляции .cpp. Если компилятор может оптимизировать существование класса, при условии, что программа ведет себя так же без него, его можно исключить из выходного исполняемого файла.
C имеет стандартизированный двоичный интерфейс. Поэтому вы сможете узнать, что ваши функции экспортируются. Но в C ++ такого стандарта нет.
Забавно, сколько более широких вопросов это объявление вскопало. Дэн Лайк на своем веб-сайте Flutterby провел очень занимательную и вдумчивую дискуссию о разработке межпроцессных стандартов как способа начальной загрузки новых языков (и других разветвлений, но это уместно здесь).
Это может быть достигнуто с помощью команды cgo.
По сути, «если импорту« C »предшествует комментарий, этот комментарий, называемый преамбулой, используется в качестве заголовка при компиляции частей C пакета. Например: '
источник: https://golang.org/cmd/cgo/
// #include <stdio.h>
// #include <errno.h>
import "C"