C से गो कार्य को कॉल करें


150

मैं गो प्रोग्राम में C प्रोग्राम (जैसे, कर्नेल मॉड्यूल या कुछ और) के साथ एक स्थिर ऑब्जेक्ट बनाने की कोशिश कर रहा हूं।

मैंने गो से C फ़ंक्शन को कॉल करने के बारे में प्रलेखन पाया है, लेकिन मुझे दूसरे तरीके से जाने के लिए बहुत कुछ नहीं मिला है। मैंने पाया है कि यह संभव है, लेकिन जटिल है।

जो मुझे मिला वह यहां है:

C और Go के बीच कॉलबैक के बारे में ब्लॉग पोस्ट

Cgo प्रलेखन

गोलंग मेलिंग सूची पोस्ट

क्या किसी के पास इसका अनुभव है? संक्षेप में, मैं पूरी तरह से गो में लिखा एक PAM मॉड्यूल बनाने की कोशिश कर रहा हूं।


11
आप कम से कम उन धागों से नहीं हो सकते जो गो से नहीं बने थे। मैंने इसके बारे में कई बार हंगामा किया है, और जब तक यह तय नहीं हो जाता है तब तक गो में विकसित नहीं हुआ है।
मैट जॉइनर

मैंने सुना कि यह संभव है। क्या कोई हल नहीं है?
बीटगैमिट

गो एक अलग कॉलिंग कन्वेंशन और सेगमेंटेड स्टैक का उपयोग करता है। आप सी कोड के साथ gccgo के साथ संकलित गो कोड को लिंक करने में सक्षम हो सकते हैं, लेकिन मैंने यह कोशिश नहीं की है क्योंकि मैंने अपने सिस्टम पर निर्माण के लिए gccgo नहीं प्राप्त किया है।
mkb

मैं इसे अब SWIG का उपयोग करके देख रहा हूँ, और मैं आशान्वित हूँ ... मैंने अभी तक काम करने के लिए कुछ भी हासिल नहीं किया है ... = '(मैंने मेलिंग सूची पर पोस्ट किया है। उम्मीद है कि किसी को मुझ पर दया
आए

2
आप C से गो कोड को कॉल कर सकते हैं , लेकिन फिलहाल आप Go रनटाइम को C ऐप में एम्बेड नहीं कर सकते हैं , जो एक महत्वपूर्ण, लेकिन सूक्ष्म, अंतर है।
टायलर

जवाबों:


126

आप सी से गो कोड कह सकते हैं। यह एक भ्रामक प्रस्ताव है।

प्रक्रिया उस ब्लॉग पोस्ट में उल्लिखित है जिसे आपने लिंक किया है। लेकिन मैं देख सकता हूँ कि यह कैसे बहुत उपयोगी नहीं है। यहां बिना किसी अनावश्यक बिट्स के एक छोटा स्निपेट है। इसे चीजों को थोड़ा स्पष्ट करना चाहिए।

package foo

// extern int goCallbackHandler(int, int);
//
// static int doAdd(int a, int b) {
//     return goCallbackHandler(a, b);
// }
import "C"

//export goCallbackHandler
func goCallbackHandler(a, b C.int) C.int {
    return a + b
}

// This is the public function, callable from outside this package.
// It forwards the parameters to C.doAdd(), which in turn forwards
// them back to goCallbackHandler(). This one performs the addition
// and yields the result.
func MyAdd(a, b int) int {
   return int( C.doAdd( C.int(a), C.int(b)) )
}

वह क्रम जिसमें सब कुछ कहा जाता है:

foo.MyAdd(a, b) ->
  C.doAdd(a, b) ->
    C.goCallbackHandler(a, b) ->
      foo.goCallbackHandler(a, b)

यहाँ याद रखने की कुंजी यह है कि एक कॉलबैक फ़ंक्शन को //exportगो साइड externपर और सी साइड पर टिप्पणी के साथ चिह्नित किया जाना चाहिए । इसका मतलब यह है कि आप जिस भी कॉलबैक का उपयोग करना चाहते हैं, उसे आपके पैकेज के अंदर परिभाषित किया जाना चाहिए।

कस्टम पैकेजबैक फ़ंक्शन की आपूर्ति करने के लिए आपके पैकेज के उपयोगकर्ता को अनुमति देने के लिए, हम उपरोक्त के समान सटीक दृष्टिकोण का उपयोग करते हैं, लेकिन हम उपयोगकर्ता के कस्टम हैंडलर की आपूर्ति करते हैं (जो कि एक नियमित गो फ़ंक्शन है) सी पर पारित होने वाले पैरामीटर के रूप में। पक्ष के रूप में void*। यह तब कॉलबैकहैंडलर द्वारा हमारे पैकेज में प्राप्त किया जाता है और कहा जाता है।

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

type ProgressHandler func(current, total uint64, userdata interface{}) int

यह हैंडलर इंटरफ़ेस {} मान के साथ कुछ प्रगति की जानकारी (प्राप्त फ़ाइलों की कुल संख्या और फ़ाइलों की कुल संख्या) लेता है, जिसे उपयोगकर्ता को रखने के लिए किसी भी चीज़ की आवश्यकता हो सकती है।

अब हमें इस हैंडलर का उपयोग करने की अनुमति देने के लिए सी और गो प्लंबिंग लिखना होगा। सौभाग्य से C फ़ंक्शन जिसे मैं लाइब्रेरी से कॉल करना चाहता हूं, वह हमें उपयोगकर्ता प्रकार की संरचना में पास करने की अनुमति देता है void*। इसका मतलब यह है कि हम इसे पकड़ सकते हैं जो हम इसे पकड़ना चाहते हैं, कोई सवाल नहीं पूछा गया है और हम इसे वापस गो-वर्ल्ड में ले जाएंगे। यह सब काम करने के लिए, हम लाइब्रेरी फ़ंक्शन को सीधे गो से नहीं कहते हैं, लेकिन हम इसके लिए एक सी रैपर बनाते हैं जिसे हम नाम देंगे goGetFiles()। यह एक रैपर है जो वास्तव में सी लाइब्रेरी के लिए हमारे गो कॉलबैक की आपूर्ति करता है, साथ ही एक यूजरडेटा ऑब्जेक्ट भी।

package foo

// #include <somelib.h>
// extern int goProgressCB(uint64_t current, uint64_t total, void* userdata);
// 
// static int goGetFiles(some_t* handle, void* userdata) {
//    return somelib_get_files(handle, goProgressCB, userdata);
// }
import "C"
import "unsafe"

ध्यान दें कि goGetFiles()फ़ंक्शन कॉलबैक के लिए किसी भी फ़ंक्शन पॉइंट को पैरामीटर के रूप में नहीं लेता है। इसके बजाय, हमारे उपयोगकर्ता ने जो कॉलबैक दिया है वह कस्टम संरचना में पैक किया गया है जो उस हैंडलर और उपयोगकर्ता के स्वयं के उपयोगकर्ता मूल्य दोनों को रखता है। हम इसे goGetFiles()userdata पैरामीटर के रूप में पास करते हैं ।

// This defines the signature of our user's progress handler,
type ProgressHandler func(current, total uint64, userdata interface{}) int 

// This is an internal type which will pack the users callback function and userdata.
// It is an instance of this type that we will actually be sending to the C code.
type progressRequest struct {
   f ProgressHandler  // The user's function pointer
   d interface{}      // The user's userdata.
}

//export goProgressCB
func goProgressCB(current, total C.uint64_t, userdata unsafe.Pointer) C.int {
    // This is the function called from the C world by our expensive 
    // C.somelib_get_files() function. The userdata value contains an instance
    // of *progressRequest, We unpack it and use it's values to call the
    // actual function that our user supplied.
    req := (*progressRequest)(userdata)

    // Call req.f with our parameters and the user's own userdata value.
    return C.int( req.f( uint64(current), uint64(total), req.d ) )
}

// This is our public function, which is called by the user and
// takes a handle to something our C lib needs, a function pointer
// and optionally some user defined data structure. Whatever it may be.
func GetFiles(h *Handle, pf ProgressFunc, userdata interface{}) int {
   // Instead of calling the external C library directly, we call our C wrapper.
   // We pass it the handle and an instance of progressRequest.

   req := unsafe.Pointer(&progressequest{ pf, userdata })
   return int(C.goGetFiles( (*C.some_t)(h), req ))
}

यह हमारे सी बाइंडिंग के लिए है। उपयोगकर्ता का कोड अब बहुत सीधा है:

package main

import (
    "foo"
    "fmt"
)

func main() {
    handle := SomeInitStuff()

    // We call GetFiles. Pass it our progress handler and some
    // arbitrary userdata (could just as well be nil).
    ret := foo.GetFiles( handle, myProgress, "Callbacks rock!" )

    ....
}

// This is our progress handler. Do something useful like display.
// progress percentage.
func myProgress(current, total uint64, userdata interface{}) int {
    fc := float64(current)
    ft := float64(total) * 0.01

    // print how far along we are.
    // eg: 500 / 1000 (50.00%)
    // For good measure, prefix it with our userdata value, which
    // we supplied as "Callbacks rock!".
    fmt.Printf("%s: %d / %d (%3.2f%%)\n", userdata.(string), current, total, fc / ft)
    return 0
}

यह सब एक बहुत अधिक जटिल से अधिक लग रहा है। कॉल ऑर्डर हमारे पिछले उदाहरण के विपरीत नहीं बदला गया है, लेकिन हमें श्रृंखला के अंत में दो अतिरिक्त कॉल मिलते हैं:

आदेश इस प्रकार है:

foo.GetFiles(....) ->
  C.goGetFiles(...) ->
    C.somelib_get_files(..) ->
      C.goProgressCB(...) ->
        foo.goProgressCB(...) ->
           main.myProgress(...)

हां, मुझे एहसास है कि यह सब हमारे चेहरे पर उड़ सकता है जब अलग-अलग धागे खेल में आते हैं। विशेष रूप से जो गो द्वारा निर्मित नहीं हैं। दुर्भाग्य से इस तरह से चीजें इस बिंदु पर हैं।
jimt

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

@jimt कचरा संग्राहक के साथ कैसे एकीकृत होता है? विशेष रूप से, निजी प्रगति कब तक एकत्रित की जाती है? (नई जाने के लिए, और इस प्रकार असुरक्षित करने के लिए। सूचक)। इसके अलावा, SQLite3 जैसे API के बारे में क्या है जो एक void * userdata लेता है, लेकिन userataata के लिए एक वैकल्पिक "deleter" फ़ंक्शन भी है? क्या इसका उपयोग GC के साथ बातचीत करने के लिए किया जा सकता है, यह बताने के लिए कि "अब उस उपयोगकर्ता को पुनः प्राप्त करना ठीक है, बशर्ते कि गो पक्ष इसे संदर्भित नहीं करता है?"।
डेडविने

6
गो 1.5 के रूप में कॉलिंग के लिए बेहतर सपोर्ट है। गो से सी। इस प्रश्न को देखें यदि आप एक ऐसे उत्तर की तलाश में हैं जो एक सरल तकनीक को प्रदर्शित करता है: stackoverflow.com/questions/32215509/…
गेब्रियल दक्षिणी

2
गो 1.6 के रूप में यह दृष्टिकोण काम नहीं करता है, यह "C कोड कॉल रिटर्न के बाद गो पॉइंटर की एक प्रति नहीं रख सकता है।" नियम और "पैनिक: रनटाइम एरर: कॉगो तर्क में पॉइंटर टू गो प्वाइंटर" एरर है, रनटाइम पर जारी करता है
कास्पेर्सकी

56

यह एक भ्रामक प्रस्ताव नहीं है यदि आप gccgo का उपयोग करते हैं। यह यहाँ काम करता है:

foo.go

package main

func Add(a, b int) int {
    return a + b
}

bar.c

#include <stdio.h>

extern int go_add(int, int) __asm__ ("example.main.Add");

int main() {
  int x = go_add(2, 3);
  printf("Result: %d\n", x);
}

makefile

all: main

main: foo.o bar.c
    gcc foo.o bar.c -o main

foo.o: foo.go
    gccgo -c foo.go -o foo.o -fgo-prefix=example

clean:
    rm -f main *.o

जब मैं जाने के लिए कोड स्ट्रिंग के साथ sth go package main func Add(a, b string) int { return a + b }मैं त्रुटि "अपरिभाषित _go_string_plus" मिलता है
TruongSinh

2
TruongSinh, आप शायद का उपयोग करना चाहते हैं cgoऔर goइसके बजाय gccgoGolang.org/cmd/cgo देखें । जब यह कहा जाता है, तो .go फ़ाइल में "स्ट्रिंग" प्रकार का उपयोग करना और __go_string_plusफ़ंक्शन को शामिल करने के लिए अपनी .c फ़ाइल को बदलना पूरी तरह से संभव है । यह काम करता है: ix.io/dZB
अलेक्जेंडर

12

गो 1.5 की रिलीज़ के साथ उत्तर बदल गया है

यह एसओ का सवाल है जो मैंने कुछ समय पहले पूछा था कि 1.5 जोड़ा क्षमताओं के प्रकाश में इस मुद्दे को फिर से संबोधित करता है

किसी मौजूदा C प्रोजेक्ट में गो कोड का उपयोग करना


3

जहां तक ​​मेरा संबंध है यह संभव नहीं है:

ध्यान दें: यदि आप निर्यात का उपयोग कर रहे हैं तो आप प्रस्तावना में कोई C फ़ंक्शन परिभाषित नहीं कर सकते

source: https://github.com/golang/go/wiki/cgo

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.