Go kodunu C'den arayabilirsiniz. Yine de kafa karıştırıcı bir önermedir.
İşlem, bağlantı kurduğunuz blog yayında özetlenmiştir. Ama bunun nasıl yardımcı olmadığını görebiliyorum. İşte gereksiz bitler olmadan kısa bir snippet. İşleri biraz daha netleştirmeli. aşağıdaki gibi
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şey denir sıradır:
foo.MyAdd(a, b) ->
C.doAdd(a, b) ->
C.goCallbackHandler(a, b) ->
foo.goCallbackHandler(a, b)
burada unutulmaması gereken önemli bir geri çağırma işlevi Git tarafında //export
yorum ile ve üzerinde extern
olarak işaretlenmesi gerekir ki C tarafı. Bu, kullanmak istediğiniz herhangi bir geri dönüşün paketinizin içinde tanımlanması gerektiği anlamına gelir.
Paketinizin bir kullanıcısının özel bir geri arama işlevi sağlamasına izin vermek için, yukarıdakiyle tam olarak aynı yaklaşımı kullanırız, ancak kullanıcının özel işleyicisini (yalnızca normal bir Go işlevi olan) bir parametre olarak sağlarız. C tarafına void*
olarak geçirilmiştir. Daha sonra paketteki callbackhandler tarafından alınır ve aranır.
Şu anda üzerinde çalıştığım daha gelişmiş bir örnek kullanalım. Bu durumda, oldukça ağır bir görev gerçekleştiren bir C fonksiyonuna sahibiz: Bir USB cihazındaki dosyaların bir listesini okur. Bu biraz zaman alabilir, bu nedenle uygulamanızın ilerlemesini bildirmesini istiyoruz. Bunu programımızda tanımladığımız bir işlev işaretçisini kullanarak yapabiliriz. Sadece çağrıldığında kullanıcıya bazı ilerleme bilgileri gösterir. bu bilinen bir imzası vardır yana, bunu kendi türünü atayabilirsiniz:
type ProgressHandler func(current, total uint64, userdata interface{}) int
Bu işleyici bir arayüz {} değeri ile birlikte bazı ilerlemeler bilgi (dosya mevcut sayısı alınan ve dosyaların toplam sayısı) aldığı can Kullanıcının tutması gereken herhangi bir şeyi tutmak.
Şimdi bu işleyiciyi kullanmamıza izin vermek için C ve Go tesisatını yazmamız gerekiyor. Neyse ki kütüphaneden aramak istediğim C işlevi, void*
türünde bir userdata yapısında geçiş yapmamıza izin veriyor. Bu, onu tutmak istediğimiz her şeyi tutabileceği, hiçbir soru sorulmadığı ve Go dünyasında olduğu gibi geri alabileceğimiz anlamına gelir. Tüm bu çalışmaları yapmak için doğrudan Go kütüphanesi işlevini çağırmıyoruz, ancak bunun için goGetFiles()
adını vereceğimiz bir C sarıcı oluşturuyoruz. Aslında, Go geri bildirimini C kütüphanesine ve bir userdata nesnesiyle birlikte sunan bu sarmalayıcıdır.goGetFiles()
fonksiyon parametreleri olarak geri aramalar için herhangi bir fonksiyon işaretçileri sürmüyor
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"
Not. Bunun yerine, kullanıcımızın sağladığı geri arama, hem bu işleyiciyi hem de kullanıcının kendi kullanıcı veri değerini bulunduran özel bir yapıda paketlenmiştir. Bunu userdata parametresi olarak goGetFiles()
'a aktarıyoruz.
// 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 geçerlidir. Kullanıcının kod çok yalındır artık şudur:
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
}
Bunların hepsi o çok daha karmaşık görünüyor. Önceki örneğe aksine çağrı sırası değişmedi, ama biz zincirin ucunda iki ekstra aramaları almak:
foo.GetFiles(....) ->
C.goGetFiles(...) ->
C.somelib_get_files(..) ->
C.goProgressCB(...) ->
foo.goProgressCB(...) ->
main.myProgress(...)
En azından Go tarafından oluşturulmamış iş parçacıklarından yapamazsınız. Bu sayısız zamandan ötürü öfkelendim ve bu düzeltilene kadar Go'da gelişmeyi kestim. –
Bunun mümkün olduğunu duydum. Çözüm yok mu? – tjameson
Go, farklı bir çağrı kuralı ve bölümlere ayrılmış yığınlar kullanır. Gccgo ile derlenmiş Go kodunu C koduyla ilişkilendirebilirsiniz, ancak bu sistemi denemedim, çünkü gccgo'yu sistemimde oluşturmaya çalışmadım. – mkb