সি থেকে ফোকাস কল করুন


150

আমি সি প্রোগ্রামের সাথে গো (ইন্টারফেস) তে লিখিত একটি স্ট্যাটিক অবজেক্ট তৈরি করার চেষ্টা করছি (বলুন, কার্নেল মডিউল বা কিছু)।

আমি গো থেকে সি ফাংশন কল করার বিষয়ে ডকুমেন্টেশন পেয়েছি, তবে কীভাবে অন্য পথে যেতে হয় সে সম্পর্কে খুব বেশি কিছু পাইনি। যা আমি পেয়েছি তা সম্ভব, তবে জটিল,

আমি যা পেয়েছি তা এখানে:

সি এবং গো এর মধ্যে কলব্যাক সম্পর্কে ব্লগ পোস্ট

কগো ডকুমেন্টেশন

গোলং মেলিং তালিকা পোস্ট

কাহারো কি এটির সাথে অভিজ্ঞতা আছে? সংক্ষেপে, আমি গো তে সম্পূর্ণ লিখিত একটি পিএএম মডিউল তৈরি করার চেষ্টা করছি।


11
আপনি পারবেন না, কমপক্ষে থ্রেড থেকে যা গো থেকে তৈরি হয়নি। আমি এই বহুবার ছড়িয়েছি এবং এটি স্থির না হওয়া পর্যন্ত গোতে বিকাশ বন্ধ করে দিয়েছি।
ম্যাট জয়েনার

শুনেছি এটা সম্ভব। কোন সমাধান নেই?
বেটগ্যামিত 25'11

গোতে একটি ভিন্ন কলিং কনভেনশন এবং সেগমেন্টযুক্ত স্ট্যাক ব্যবহার করা হয়। আপনি সি কোডের সাথে জিসিসিগো দিয়ে সংকলিত গো কোডটি লিঙ্ক করতে সক্ষম হতে পারেন, তবে আমি আমার সিস্টেমে তৈরির জন্য জিসিসিগো অর্জন না করায় এটি চেষ্টা করিনি।
এম কে বি

আমি এখন SWIG ব্যবহার করে এটি চেষ্টা করে দেখছি, এবং আমি আশাবাদী ... যদিও আমি এখনও কিছু করার জন্য কিছু পাইনি ... = '(আমি
মেলিংয়ের

2
আপনি সি থেকে গো কোড কল করতে পারেন , তবে এই মুহুর্তে আপনি গো রানটাইমটিকে কোনও সি অ্যাপে এম্বেড করতে পারবেন না , এটি একটি গুরুত্বপূর্ণ, তবে সূক্ষ্ম, পার্থক্য।
টাইলার

উত্তর:


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*। এটি তখন আমাদের প্যাকেজে কলব্যান্ডলার দ্বারা গ্রহণ করা এবং কল করা হয়।

আসুন আমি বর্তমানে আরও বেশি উন্নত উদাহরণ ব্যবহার করছি with এই ক্ষেত্রে, আমাদের একটি সি ফাংশন রয়েছে যা একটি ভারী ভারী কাজ সম্পাদন করে: এটি একটি ইউএসবি ডিভাইস থেকে ফাইলগুলির একটি তালিকা পড়ে। এটি কিছুটা সময় নিতে পারে, তাই আমরা চাই আমাদের অ্যাপটিকে এর অগ্রগতি সম্পর্কে অবহিত করা হোক। আমরা আমাদের প্রোগ্রামে সংজ্ঞায়িত ফাংশন পয়েন্টারটিতে যাবার মাধ্যমে এটি করতে পারি। এটি যখনই কল হবে তখন এটি কিছু অগ্রগতি সম্পর্কিত তথ্য প্রদর্শন করে। যেহেতু এটির একটি সুপরিচিত স্বাক্ষর রয়েছে, তাই আমরা এটির নিজস্ব প্রকারটি নির্ধারণ করতে পারি:

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

এই হ্যান্ডলারটি একটি ইন্টারফেস}} মান সহ কিছু অগ্রগতি তথ্য (ফাইলগুলির প্রাপ্ত নম্বর এবং ফাইলগুলির মোট সংখ্যা) নেয় যা ব্যবহারকারীকে ধরে রাখার জন্য যা কিছু প্রয়োজন তা ধরে রাখতে পারে।

আমাদের এই হ্যান্ডলারটি ব্যবহারের অনুমতি দেওয়ার জন্য এখন আমাদের সি এবং গো প্লাম্বিং লিখতে হবে। ভাগ্যক্রমে সি ফাংশনটি আমি লাইব্রেরি থেকে কল করতে চাই আমাদের প্রকারভেদে স্ট্র্যাটে স্ট্রোক করার অনুমতি দেয় 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()এটি ব্যবহারকারিতা পরামিতি হিসাবে প্রেরণ করি ।

// 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(...)

হ্যাঁ, আমি বুঝতে পারি যে পৃথক থ্রেড কার্যকর হলে এই সমস্ত কিছু আমাদের মুখের মধ্যে ফুঁসে উঠতে পারে। বিশেষ করে গো দ্বারা তৈরি করা হয়নি। দুর্ভাগ্যক্রমে বিষয়গুলি এই মুহুর্তে।
জিমট

17
এটি সত্যিই একটি ভাল উত্তর, এবং সম্পূর্ণ। এটি সরাসরি প্রশ্নের উত্তর দেয় না, তবে কারণ কোনও উত্তর নেই। বেশ কয়েকটি সূত্রের মতে, এন্ট্রি পয়েন্টটি যেতে হবে, এবং সি হতে পারে না I'm আমি এটিকে সঠিক হিসাবে চিহ্নিত করছি কারণ এটি আমার পক্ষে সত্যিকার অর্থে পরিষ্কার করা জিনিস। ধন্যবাদ!
বেটগ্যামিট

@ জিমট কীভাবে এটি আবর্জনা সংগ্রাহকের সাথে সংহত করে? বিশেষত, ব্যক্তিগত অগ্রগতি রিকোয়েস্ট উদাহরণটি কখন সংগ্রহ করা হয়? (নতুন তে যান, এবং এইভাবে অনিরাপদ.পয়েন্টার) to এছাড়াও, এসকিউএলআইটি 3 এর মতো এপিআই সম্পর্কে কী যা অকার্যকর * ইউজারডাটা গ্রহণ করে, তবে ইউজারডাটার জন্য একটি alচ্ছিক "মুছে ফেলা" ফাংশনটি কী? জিসির সাথে ইন্টারঅ্যাক্ট করার জন্য কী এটি ব্যবহার করা যেতে পারে, "এখন এই ইউজারডাটা পুনরায় দাবি করা ঠিক আছে, যদি গো সাইডটি আর রেফারেন্স না দেয়?"।
ddevienne

6
Go 1.5 এর হিসাবে সি থেকে গো কল করার পক্ষে আরও ভাল সমর্থন রয়েছে যদি আপনি একটি সহজ কৌশলটি দেখায় এমন কোনও উত্তর খুঁজছেন তবে এই প্রশ্নটি দেখুন: স্ট্রোকভারফ্লো
গ্যাব্রিয়েল সাউদার্ন

2
Go 1.6 হিসাবে এই পদ্ধতিটি কাজ করে না, এটি "সি কোড কল কল ফেরত যাওয়ার পরে গো পয়েন্টারের একটি অনুলিপি রাখতে পারে না" s নিয়ম এবং একটি "আতঙ্ক: রানটাইম ত্রুটি: সিজিও আর্গুমেন্টে গো পয়েন্টার টু গো পয়েন্টার রয়েছে" রানটাইম এ ত্রুটি
ক্যাস্পারস্কি

56

আপনি যদি সিসিসিগো ব্যবহার করেন তবে এটি বিভ্রান্তিকর প্রস্তাব নয়। এটি এখানে কাজ করে:

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

যখন আমি স্ট্রিং সহ কোড করি তখন আমি go package main func Add(a, b string) int { return a + b }ত্রুটি পাই "অপরিজ্ঞাত _গো_স্ট্রিং_প্লাস"
ট্রুংসিংহ

2
ট্রুংসিংহ, আপনি সম্ভবত ব্যবহার করতে চান cgoএবং এর goপরিবর্তে gccgoGolang.org/cmd/cgo দেখুন । যখন এটি বলা হয়, .go ফাইলটিতে "স্ট্রিং" প্রকারটি ব্যবহার করা এবং কোনও __go_string_plusফাংশন অন্তর্ভুক্ত করতে আপনার .c ফাইল পরিবর্তন করা সম্পূর্ণভাবে সম্ভব । এটি কাজ করে: ix.io/dZB
আলেকজান্ডার

12

উত্তরটি 1.5 এর প্রকাশের সাথে পরিবর্তিত হয়েছে

এই এসও প্রশ্নটি যা আমি কিছুক্ষণ আগে জিজ্ঞাসা করেছি 1.5 এর সাথে যুক্ত 1.5 টি সামর্থ্যের আলোকে এই সমস্যাটিকে আবার সম্বোধন করে

বিদ্যমান সি প্রকল্পে গো কোড ব্যবহার করা


আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.