गो में C ++ का उपयोग कैसे करें


173

नई गो भाषा में, मैं C ++ कोड कैसे कह सकता हूं? दूसरे शब्दों में, मैं अपनी C ++ कक्षाओं को कैसे लपेट सकता हूं और उन्हें गो में उपयोग कर सकता हूं?


1
टेक टॉक में SWIG का बहुत ही संक्षिप्त रूप से उल्लेख किया गया था, "..अंतिल हम
स्वाइप करवाते हैं

1
@ मैट: संभवतः वह किसी मौजूदा C ++ लाइब्रेरी को C या Go पर पोर्ट किए बिना उपयोग करना चाहता है। मुझे वही चाहिए था।
ग्रीम पेरो

मैं सी ++ के लिए उपलब्ध एक भी सभ्य पुस्तकालय के बारे में नहीं सोच सकता हूं और सी के लिए नहीं। मुझे यह जानना अच्छा लगेगा कि आपके मन में क्या है।
मैट जॉइनर

13
@ मैट: एक उदाहरण बूस्ट लाइब्रेरी है, और हजारों अन्य उपयोगी सी ++ लाइब्रेरी हैं। लेकिन शायद मैं यहाँ एक ट्रोल खिला रहा हूँ ...
फ्रैंक

@ मैट: मेरे मामले में, मैं अपने मौजूदा क्लाइंट लाइब्रेरी में गो इंटरफ़ेस बनाना चाहता था लेकिन लाइब्रेरी मुख्य रूप से C ++ है। इसे C या Go में पोर्ट करना केवल एक विकल्प नहीं है।
ग्रीम पेरो

जवाबों:


154

अपडेट: मैंने एक छोटे परीक्षण C ++ क्लास को गो के साथ लिंक करने में सफलता पाई है

यदि आप एक C इंटरफ़ेस के साथ C ++ कोड लपेटते हैं, तो आपको अपनी लाइब्रेरी को cgo के साथ कॉल करने में सक्षम होना चाहिए (gmp का उदाहरण देखें $GOROOT/misc/cgo/gmp)।

मुझे यकीन नहीं है कि अगर सी ++ में एक वर्ग का विचार वास्तव में गो में अभिव्यक्त होता है, क्योंकि इसमें विरासत नहीं है।

यहाँ एक उदाहरण है:

मेरे पास एक 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;
}

जो मैं गो में उपयोग करना चाहता हूं। मैं सी इंटरफेस का उपयोग करूँगा

// foo.h
#ifdef __cplusplus
extern "C" {
#endif
  typedef void* Foo;
  Foo FooInit(void);
  void FooFree(Foo);
  void FooBar(Foo);
#ifdef __cplusplus
}
#endif

(मैं void*एक सी संरचना के बजाय का उपयोग करता हूं ताकि कंपाइलर फू के आकार को जानता है)

कार्यान्वयन है:

//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();
}

सभी के साथ किया, जाओ फ़ाइल है:

// 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
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();
}

आपको साझा लाइब्रेरी स्थापित करने की आवश्यकता होगी, फिर मेक टेस्ट चलाएं। अपेक्षित उत्पादन है:

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

1
इसके साथ सावधान रहें, मुझे पता नहीं है कि यदि आप इसे दो भाषाओं के बीच भेजते हैं तो मेमोरी के साथ क्या हो सकता है।
स्कॉट वेल्स

11
मैं कहना चाहता हूं, यह उदाहरण मुझे याद दिलाता है कि मैं शुद्ध गो क्यों लिखना चाहता हूं। देखो कितना बड़ा और बदसूरत है C ++ पक्ष। Ick।
जेफ एलन

@ScottWales ने किसी भी मौके पर जो आपने Github या किसी भी चीज़ के रेपो में डाला होगा? मैं एक काम करने का उदाहरण देखना पसंद
करूंगा

7
@Arne: आप एक उत्तर को अस्वीकार नहीं करते क्योंकि यह सबसे अच्छा नहीं है। आप एक उत्तर को अस्वीकार कर देते हैं क्योंकि यह मददगार नहीं है। जब तक यह काम करता है, तब तक बेहतर समाधान होने पर भी यह उत्तर मददगार होता है।
ग्रीम पेरो

अच्छी खबर है, Go अब cpp को संकलित करेगा इसलिए मेकफाइल की आवश्यकता नहीं है। असुरक्षित। सूचक आवरण मेरे लिए काम नहीं करते थे। मेरे लिए एक मामूली संशोधन संकलित: play.golang.org/p/hKuKV51cRp मेकफाइल के go test बिना काम करना चाहिए
आकर्षित

47

लगता है कि वर्तमान में SWIG इसके लिए सबसे अच्छा समाधान है:

http://www.swig.org/Doc2.0/Go.html

यह वंशानुक्रम का समर्थन करता है और यहां तक ​​कि गो संरचना के साथ C ++ वर्ग को उप-वर्ग करने की अनुमति देता है, जब ओवरराइड विधियों को C ++ कोड में कहा जाता है, तो गो कोड निकाल दिया जाता है।

Go FAQ में C ++ के बारे में अनुभाग अपडेट किया गया है और अब SWIG का उल्लेख करता है और अब यह नहीं कहता है " क्योंकि गो कचरा एकत्र है, ऐसा करने के लिए वह नासमझ होगा, कम से कम भोलेपन से "।


9
काश इसको टक्कर देने का कोई तरीका होता। अन्य उत्तर पुराने हैं। प्लस SWIG ने
dragonx

34

आप अक्सर पूछे जाने वाले प्रश्न में जो कुछ भी पढ़ते हैं उससे आप अभी तक नहीं कर सकते हैं :

क्या कार्यक्रम C / C ++ प्रोग्राम के साथ लिंक करते हैं?

दो गो संकलक कार्यान्वयन, gc (6g प्रोग्राम और मित्र) और gccgo हैं। Gc एक अलग कॉलिंग कन्वेंशन और लिंकर का उपयोग करता है और इसलिए इसे केवल उसी कन्वेंशन का उपयोग करके C प्रोग्राम्स के साथ जोड़ा जा सकता है। ऐसा C कंपाइलर है लेकिन C ++ कंपाइलर नहीं है। Gccgo एक GCC फ्रंट-एंड है जो देखभाल के साथ GCC- संकलित C या C ++ प्रोग्राम के साथ जोड़ा जा सकता है।

कॉगो प्रोग्राम गो कोड से सी लाइब्रेरी की सुरक्षित कॉलिंग की अनुमति देने के लिए "विदेशी फ़ंक्शन इंटरफ़ेस" के लिए तंत्र प्रदान करता है। SWIG इस क्षमता को C ++ पुस्तकालयों तक पहुंचाता है।



13

मैंने स्कॉट वेल्स के उत्तर के आधार पर निम्नलिखित उदाहरण बनाया है । मैंने इसे 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एपीआई लागू करने के लिए पुल का कोड आवश्यक है 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();
}

(५) अंत में, library.goC ++ 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())
}

निम्न मेकफाइल का उपयोग करना

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"

पूरे उदाहरण के साथ GitHub रेपो से लिंक करें


धन्यवाद - यह बहुत मददगार था!
राबर्ट काउहम

11

लगता है कि यह गोलंग के बारे में पूछे गए शुरुआती प्रश्नों में से एक है। और उसी समय जवाब कभी अद्यतन नहीं करने के लिए। इन तीन से चार वर्षों के दौरान, कई नए पुस्तकालय और ब्लॉग पोस्ट बाहर हो गए हैं। नीचे कुछ लिंक दिए गए हैं जो मुझे उपयोगी लगे।

SWIG और जाओ

C ++ कोड को गो से SWIG के साथ कॉल करना

भाषाओं की तुलना करने पर, C ++ और Go

GoForCPPProgrammers


3

Gcc गो संकलक, gccgo का उपयोग करते समय C और Go के बीच अंतर के बारे में बात होती है। हालांकि, gccgo का उपयोग करते समय Go की इंटरऑपरेबिलिटी और कार्यान्वित फीचर सेट दोनों की सीमाएं हैं, हालांकि, (उदाहरण के लिए, सीमित गोरोइन, कोई कचरा संग्रह नहीं)।


2
1. मैनुअल मेमोरी प्रबंधन के लिए कोई सुविधा नहीं के साथ एक भाषा बनाओ, 2. कचरा संग्रह निकालें? क्या मैं इस पर केवल अपना सिर खुजला रहा हूं?
ग्योर्गी आंद्रेसेक

2

आप यहाँ अज्ञात क्षेत्र पर चल रहे हैं। सी कोड को कॉल करने के लिए यहां गो उदाहरण दिया गया है, शायद आप ऐसा कुछ कर सकते हैं कि सी ++ नाम के प्रबंध पर पढ़ना और सम्मेलनों को कॉल करना, और बहुत सारे परीक्षण और त्रुटि।

यदि आप अभी भी इसे आज़माने का मन बना रहे हैं, तो शुभकामनाएँ।


1

यहां समस्या यह है कि एक आज्ञाकारी कार्यान्वयन को अपनी कक्षाओं को एक .cpp फ़ाइल में रखने की आवश्यकता नहीं है। यदि कंपाइलर किसी वर्ग के अस्तित्व को अनुकूलित कर सकता है, तो जब तक कार्यक्रम उसके बिना उसी तरह से व्यवहार करता है, तब तक इसे आउटपुट निष्पादन योग्य से छोड़ा जा सकता है।

C में एक मानकीकृत बाइनरी इंटरफ़ेस है। इसलिए आप यह जान पाएंगे कि आपके कार्य निर्यात किए गए हैं। लेकिन C ++ के पीछे ऐसा कोई मानक नहीं है।


1

मानक पुस्तकालय की आवश्यकता को पहचानने के लिए आपको गोलंग / सीजीओ -lc++के LDFlagsलिए जोड़ना पड़ सकता है ।


0

मजेदार यह कि इस घोषणा ने कितने व्यापक मुद्दों को जन्म दिया है। नई भाषाओं को बूटस्ट्रैप करने के तरीके के रूप में इंटरप्रोसेस मानकों को विकसित करने के बारे में डैन लिके ने अपनी वेबसाइट, फ्लटरबाई पर एक बहुत ही मनोरंजक और विचारशील चर्चा की थी (और अन्य व्यवहार है, लेकिन वह एक है जो यहां जर्मे है)।


0

यह कमांड कोगो का उपयोग करके प्राप्त किया जा सकता है।

संक्षेप में, यदि "C" का आयात तुरंत एक टिप्पणी से पहले होता है, तो उस टिप्पणी को, जिसे प्रस्तावना कहा जाता है, पैकेज के C भागों को संकलित करते समय हेडर के रूप में उपयोग किया जाता है। उदाहरण के लिए: '
स्रोत: https://golang.org/cmd/cgo/

// #include <stdio.h>
// #include <errno.h>
import "C"
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.