IT story

Go에서 C ++를 사용하는 방법?

hot-time 2020. 6. 10. 08:06
반응형

Go에서 C ++를 사용하는 방법?


새로운 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*컴파일러가 Foo의 크기를 알 수 있도록 C 구조체 대신에를 사용합니다 )

구현은 다음과 같습니다.

//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

상속을 지원하고 심지어 Go 구조체를 사용하여 C ++ 클래스를 서브 클래 싱 할 수 있으므로 C ++ 코드에서 재정의 된 메서드를 호출하면 Go 코드가 시작됩니다.

Go FAQ의 C ++에 대한 섹션 이 업데이트되었으며 이제 SWIG를 언급하고 더 이상 " Go는 가비지 수집되므로 적어도 순진하게 그렇게하는 것이 현명 하지 않습니다"라고 말하지 않습니다 .


FAQ에서 읽은 내용을 아직 읽을 수 없습니다 .

Go 프로그램은 C / C ++ 프로그램과 연결됩니까?

gc (6g 프로그램 및 친구)와 gccgo의 두 가지 Go 컴파일러 구현이 있습니다. Gc는 다른 호출 규칙과 링커를 사용하므로 동일한 규칙을 사용하여 C 프로그램과 만 연결할 수 있습니다. 이러한 C 컴파일러는 있지만 C ++ 컴파일러는 없습니다. Gccgo는주의해서 GCC 컴파일 C 또는 C ++ 프로그램과 연결될 수있는 GCC 프론트 엔드입니다.

cgo 프로그램은 Go 코드에서 C 라이브러리를 안전하게 호출 할 수 있도록 "외부 기능 인터페이스"메커니즘을 제공합니다. SWIG는이 기능을 C ++ 라이브러리로 확장합니다.


go1.2 +부터 cgo는 C ++ 코드를 자동으로 통합하고 컴파일합니다.

http://golang.org/doc/go1.2#cgo_and_cpp


Golang에 대한 초기 질문 중 하나 인 것 같습니다. 그리고 동시에 업데이 트하지 않는 답변. 이 3-4 년 동안 너무 많은 새로운 도서관과 블로그 게시물이 나왔습니다. 아래는 제가 유용하다고 생각한 몇 가지 링크입니다.

SWIG and Go

SWIG와 함께 C ++ 코드 호출

언어 비교, C ++ 및 Go

GoForCPP 프로그래머


Scott Wales의 답변을 기반으로 다음 예제를 만들었습니다 . macOS High Sierra 10.13.3 running goversion 에서 테스트했습니다 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.cppC ++ 구현을 위한 코드 .

#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브리지를 위한 코드 CAPI 를 사용할 수 C++있도록 구현 된 API 를 공개 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) Finally, library.go, the go program calling the 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())
}

Using the following 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

I can run the example program as follows:

$ 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)

Important

The comments above import "C" in the go program are NOT OPTIONAL. You must put them exactly as shown so that cgo knows which header and library to load, in this case:

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

Link to GitHub repo with the full example.


There's talk about interoperability between C and Go when using the gcc Go compiler, gccgo. There are limitations both to the interoperability and the implemented feature set of Go when using gccgo, however (e.g., limited goroutines, no garbage collection).


You're walking on uncharted territory here. Here is the Go example for calling C code, perhaps you can do something like that after reading up on C++ name mangling and calling conventions, and lots of trial and error.

If you still feel like trying it, good luck.


The problem here is that a compliant implementation does not need to put your classes in a compile .cpp file. If the compiler can optimize out the existence of a class, so long as the program behaves the same way without it, then it can be omitted from the output executable.

C has a standardized binary interface. Therefore you'll be able to know that your functions are exported. But C++ has no such standard behind it.


Funny how many broader issues this announcement has dredged up. Dan Lyke had a very entertaining and thoughtful discussion on his website, Flutterby, about developing Interprocess Standards as a way of bootstrapping new languages (and other ramifications, but that's the one that is germane here).


You might need to add -lc++ to the LDFlags for Golang/CGo to recognize the need for the standard library.


This can be achieved using command cgo.

In essence 'If the import of "C" is immediately preceded by a comment, that comment, called the preamble, is used as a header when compiling the C parts of the package. For example:'
source:https://golang.org/cmd/cgo/

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

참고URL : https://stackoverflow.com/questions/1713214/how-to-use-c-in-go

반응형