Dalam bahasa Go yang baru , bagaimana cara saya memanggil kode C ++? Dengan kata lain, bagaimana saya bisa membungkus kelas C ++ saya dan menggunakannya di Go?
Dalam bahasa Go yang baru , bagaimana cara saya memanggil kode C ++? Dengan kata lain, bagaimana saya bisa membungkus kelas C ++ saya dan menggunakannya di Go?
Jawaban:
Pembaruan: Saya telah berhasil menghubungkan tes C ++ kelas kecil dengan Go
Jika Anda membungkus Anda kode C ++ dengan antarmuka C Anda harus dapat memanggil perpustakaan Anda dengan cgo (lihat contoh gmp in $GOROOT/misc/cgo/gmp
).
Saya tidak yakin apakah gagasan tentang kelas di C ++ benar-benar dapat diekspresikan di Go, karena tidak memiliki warisan.
Ini sebuah contoh:
Saya memiliki kelas C ++ yang didefinisikan sebagai:
// 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;
}
yang ingin saya gunakan di Go. Saya akan menggunakan antarmuka C.
// foo.h
#ifdef __cplusplus
extern "C" {
#endif
typedef void* Foo;
Foo FooInit(void);
void FooFree(Foo);
void FooBar(Foo);
#ifdef __cplusplus
}
#endif
(Saya menggunakan void*
bukan C struct sehingga kompiler tahu ukuran Foo)
Implementasinya adalah:
//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();
}
dengan semua yang dilakukan, file Go adalah:
// 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 yang saya gunakan untuk mengkompilasi ini adalah:
// 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)
Coba mengujinya dengan:
// foo_test.go
package foo
import "testing"
func TestFoo(t *testing.T){
foo := New();
foo.Bar();
foo.Free();
}
Anda harus menginstal pustaka bersama dengan make install, lalu jalankan make test. Output yang diharapkan adalah:
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
harus bekerja tanpa makefile
Tampaknya saat ini SWIG adalah solusi terbaik untuk ini:
http://www.swig.org/Doc2.0/Go.html
Ini mendukung warisan dan bahkan memungkinkan untuk mensubkelas kelas C ++ dengan Go struct sehingga ketika metode yang ditimpa dipanggil dalam kode C ++, kode Go dipecat.
Bagian tentang C ++ di Go FAQ diperbarui dan sekarang menyebutkan SWIG dan tidak lagi mengatakan " karena Go adalah sampah, tidak bijaksana untuk melakukannya, setidaknya secara naif ".
Anda belum bisa membaca apa yang saya baca di FAQ :
Apakah program Go terhubung dengan program C / C ++?
Ada dua implementasi kompilator Go, gc (program 6g dan teman-teman) dan gccgo. Gc menggunakan konvensi panggilan dan tautan yang berbeda dan karenanya hanya dapat dihubungkan dengan program C yang menggunakan konvensi yang sama. Ada kompiler C seperti itu tetapi tidak ada kompiler C ++. Gccgo adalah front-end GCC yang dengan hati-hati dapat dihubungkan dengan program C atau C ++ yang dikompilasi GCC.
Program cgo menyediakan mekanisme untuk "antarmuka fungsi asing" untuk memungkinkan panggilan yang aman dari perpustakaan C dari kode Go. SWIG memperluas kemampuan ini ke pustaka C ++.
Pada go1.2 +, cgo secara otomatis menggabungkan dan mengkompilasi kode C ++:
Saya telah membuat contoh berikut berdasarkan jawaban Scott Wales . Saya sudah mengujinya di macOS High Sierra 10.13.3 go
versi yang sedang berjalango1.10 darwin/amd64
.
(1) Kode untuk library.hpp
, API C ++ yang ingin kami panggil.
#pragma once
class Foo {
public:
Foo(int value);
~Foo();
int value() const;
private:
int m_value;
};
(2) Kode untuk library.cpp
, implementasi 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) Kode untuk library-bridge.h
jembatan diperlukan untuk mengekspos C
API yang diimplementasikan C++
sehingga go
dapat menggunakannya.
#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) Kode untuk library-bridge.cpp
, implementasi jembatan.
#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) Akhirnya,, library.go
program go memanggil C ++ API.
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())
}
Gunakan Makefile berikut
liblibrary.so: library.cpp library-bridge.cpp
clang++ -o liblibrary.so library.cpp library-bridge.cpp \
-std=c++17 -O3 -Wall -Wextra -fPIC -shared
Saya dapat menjalankan contoh program sebagai berikut:
$ 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)
Penting
Komentar import "C"
di atas dalam go
program BUKAN OPSIONAL . Anda harus meletakkannya persis seperti yang ditunjukkan sehingga cgo
tahu header dan pustaka mana yang akan dimuat, dalam hal ini:
// #cgo LDFLAGS: -L. -llibrary
// #include "library-bridge.h"
import "C"
Sepertinya itu salah satu pertanyaan awal tentang Golang. Dan jawaban yang sama untuk tidak pernah diperbarui. Selama tiga hingga empat tahun ini, terlalu banyak perpustakaan dan posting blog baru telah habis. Di bawah ini adalah beberapa tautan yang saya rasa berguna.
Memanggil Kode C ++ Dari Go With SWIG
Ada pembicaraan tentang interoperabilitas antara C dan Go saat menggunakan kompiler gcc Go, gccgo. Ada beberapa batasan untuk interoperabilitas dan set fitur Go yang diterapkan saat menggunakan gccgo, namun (misalnya, goroutine terbatas, tidak ada pengumpulan sampah).
Anda berjalan di wilayah yang belum dipetakan di sini. Berikut ini adalah contoh Go untuk memanggil kode C, mungkin Anda dapat melakukan sesuatu seperti itu setelah membaca tentang C ++ nama mangling dan konvensi pemanggilan, dan banyak trial and error.
Jika Anda masih merasa ingin mencobanya, semoga berhasil.
Masalahnya di sini adalah implementasi yang sesuai tidak perlu menempatkan kelas Anda dalam file .cpp kompilasi. Jika kompiler dapat mengoptimalkan keberadaan suatu kelas, selama program berperilaku dengan cara yang sama tanpanya, maka ia dapat dihilangkan dari output yang dapat dieksekusi.
C memiliki antarmuka biner standar. Karenanya, Anda dapat mengetahui bahwa fungsi Anda diekspor. Tetapi C ++ tidak memiliki standar seperti itu.
Lucu berapa banyak masalah yang lebih luas yang pengumuman ini dikeruk. Dan Lyke melakukan diskusi yang sangat menghibur dan penuh pertimbangan di situs webnya, Flutterby, tentang mengembangkan Standar Prosesproses sebagai cara bootstrap bahasa baru (dan konsekuensi lainnya, tapi itulah yang berhubungan erat di sini).
Ini dapat dicapai dengan menggunakan perintah cgo.
Intinya 'Jika impor "C" segera didahului dengan komentar, komentar itu, yang disebut preamble, digunakan sebagai header ketika menyusun bagian-bagian C dari paket. Misalnya: '
sumber: https://golang.org/cmd/cgo/
// #include <stdio.h>
// #include <errno.h>
import "C"