Як я можу викликати код C ++ у новій мові Go ? Іншими словами, як я можу обгортати свої C ++ класи та використовувати їх у програмі Go?
Як я можу викликати код C ++ у новій мові Go ? Іншими словами, як я можу обгортати свої C ++ класи та використовувати їх у програмі Go?
Відповіді:
Оновлення: мені вдалося зв'язати невеликий тестовий клас C ++ з Go
Якщо ви з'єднаєте код 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. Я буду використовувати інтерфейс С
// 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
повинна працювати без makefile
Здається, що на даний момент 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, але немає компілятора C ++. Gccgo - це передумови GCC, які з обережністю можуть бути пов'язані з компільованими GCC програмами C або C ++.
Програма cgo забезпечує механізм "інтерфейсу іноземних функцій", який дозволяє безпечно викликати бібліотеки C з коду Go. SWIG розширює цю можливість на бібліотеках C ++.
Станом на go1.2 +, cgo автоматично включає і компілює код C ++:
Я створив наступний приклад на основі відповіді Скотта Уельса . Я тестував його у go
версії macOS High Sierra 10.13.3go1.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"
в go
програмі, НЕ ОБОВ'ЯЗКОВІ . Ви повинні розмістити їх точно так, як показано, щоб було cgo
відомо, який заголовок та бібліотеку завантажувати, у цьому випадку:
// #cgo LDFLAGS: -L. -llibrary
// #include "library-bridge.h"
import "C"
Здається, це одне з ранніх питань про Голанг. І в той же час відповідає, що ніколи не оновлюється. Протягом цих трьох-чотирьох років надто багато нових бібліотек та публікацій в блозі не було. Нижче наведено декілька посилань, що я вважав корисним.
Виклик коду C ++ з GoIG за допомогою SWIG
Ми говоримо про сумісність між C і Go при використанні компілятора gcc Go, gccgo. Однак існують обмеження як для сумісності, так і для реалізованого набору функцій Go при використанні gccgo (наприклад, обмежені умови, відсутність збирання сміття).
Ви тут гуляєте по невідомій території. Ось приклад Go для виклику коду С, можливо, ви можете зробити щось подібне, прочитавши на маніпулювання іменем C ++ і виклику, і багато спроб та помилок.
Якщо ви все ще хочете спробувати це, удачі.
Проблема тут полягає в тому, що сумісна реалізація не потребує класів у компільований .cpp-файл. Якщо компілятор може оптимізувати існування класу до тих пір, поки програма поводиться однаково без нього, то його можна випустити з вихідного виконуваного файлу.
C має стандартизований бінарний інтерфейс. Тому ви зможете знати, що ваші функції експортуються. Але C ++ не має такого стандарту.
Смішно, наскільки більш широкі випуски затримали це оголошення. Ден Лайк провів дуже цікаву та продуману дискусію на своєму веб-сайті, Flutterby, щодо розробки Міжпроцесорних стандартів як способу завантаження нових мов (та інших розгалужень, але саме тут є німецька).
Цього можна досягти, використовуючи команду cgo.
По суті, "Якщо імпорту" C "негайно передує коментар, цей коментар, який називається преамбулою, використовується як заголовок при компілюванні частин C пакета. Наприклад: '
джерело: https://golang.org/cmd/cgo/
// #include <stdio.h>
// #include <errno.h>
import "C"