SORU
25 Mayıs 2011, ÇARŞAMBA


Ara C fonksiyonları

Statik bir nesne C programı (diyelim ki, bir çekirdek modülü veya bir şey) ile arabirim Gidin yazılmış oluşturmak için çalışıyorum.

Go C işlevlerini çağırmadan belge bulduk, ama diğer tarafa gitmek için fazla bulamadım. Bulduğum şey mümkün, ama biraz karışık.

Ne buldum burada

Blog post about callbacks between C and Go

Cgo documentation

Golang mailing list post

Kimseye bu konuda deneyimi olan var mı? Kısacası, bir PAM modülü içeri tamamen yazılı yaratmaya çalışıyorum.

CEVAP
27 Mayıs 2011, Cuma


Kafa karıştırıcı bir önermedir C. kod olsa da diyebilirsin.

Sürecine bağlantılı blog yazısı gösterilmiştir. Ama bu çok yararlı değildir. Burada gereksiz bit olmadan kısa bir Pasajı. Bazı şeyler biraz daha net yapmak gerekir.

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

Her şeyin denir sırası aşağıdaki gibidir:

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

Burada hatırlanması gereken önemli bir geri arama fonksiyonu C extern Git tarafta //export yorum ve yan işaretlenmelidir. Bu kullanmak istediğiniz geri arama, paket içinde tanımlı olmalıdır.

Sipariş için izin, bir kullanıcının paketinize kaynağı özel bir geri arama işlevi, kullandığımız aynı yaklaşımı yukarıdaki gibi, ama biz tedarik kullanıcının özel işleyici (sadece düzenli Git fonksiyonu parametre olarak geçirilen üzerine C yan void*. O zaman bizim paket callbackhandler tarafından alınan ve denir.

Hadi şu anda üzerinde çalıştığım daha gelişmiş bir örneği. Bu durumda, çok ağır bir görevi yerine getiren bir C fonksiyonu var: USB aygıtından dosyaların listesini okur. Bu bizim app ilerleme haberdar olmak istiyoruz bu yüzden bir süre alabilir. Bizim program içinde tanımlanan bir işlev işaretçisi geçerken bunu yapabiliriz. Sadece bir miktar ilerleme denir alır her kullanıcı için bilgi görüntüler. İyi bilinen bir imzası vardır beri, kendi türüne atayabiliriz:

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

Bu işleyici BİLGİ Miktar ilerleme (alınan dosyalar ve dosyaların toplam sayısı sayısı) bir arayüz ile birlikte alır{} tutmak için kullanıcının ihtiyacı olan her şeyi tutabilir değer.

Şimdi C yazma ve sıhhi tesisat bize bu işleyicisi kullanmak için izin vermek için gitmeliyiz. Neyse ki C işlev kitaplığı ABD Tür kalıcı kullanıcı verileri bir yapı void* geçmek olanak sağlar çağırmak istiyorum. Bu tutmak istediğimizi tutun, sorgusuz sualsiz anlamına gelir ve geri Gidip dünyayı olduğu gibi içine alacağız. Tüm bu işi yapmak için, Gidip kütüphane işlevi doğrudan hitap edilmez, ama goGetFiles() adını verdiğimiz bu C sarıcı yaratıyoruz. Aslında kalıcı kullanıcı verileri bir nesne ile birlikte C kütüphaneye Gidip bizim geri sağlayan bu kapsayıcı.

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() fonksiyonu parametre olarak geri aramalar için herhangi bir işlev işaretçileri almaz unutmayın. Bunun yerine, kullanıcı tarafından sağlanan bu geri çağrı işleyicisi ve kullanıcının kendi değeri hem de kalıcı kullanıcı verileri tutan özel bir yapı doludur. Kalıcı kullanıcı verileri parametre olarak goGetFiles() Bu geçiyoruz.

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

Bu bizim C bağlamaları için. Kullanıcı kodu şimdi çok basittir:

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
}

Bu çok daha karmaşık görünüyor. Arama emri önceki örneğin aksine değişmedi, ama zincirinin sonunda: iki ekstra-bir çağrı aldık

Sırası aşağıdaki gibidir:

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

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • Damian Winter

    Damian Winte

    27 ŞUBAT 2007
  • parlophone

    parlophone

    28 ŞUBAT 2006
  • williamfitzsimmons

    williamfitzs

    14 Mart 2008