You can call Go code from C. it is a confusing proposition though.
The process is outlined in the blog post you linked to. But I can see how that isn't very helpful. Here is a short snippet without any unnecessary bits. It should make things a little clearer.
package foo// extern int goCallbackHandler(int, int);//// static int doAdd(int a, int b) {// return goCallbackHandler(a, b);// }import "C"//export goCallbackHandlerfunc 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)) )}
The order in which everything is called is as follows:
foo.MyAdd(a, b) -> C.doAdd(a, b) -> C.goCallbackHandler(a, b) -> foo.goCallbackHandler(a, b)
The key to remember here is that a callback function must be marked with the //export
comment on the Go side and as extern
on the C side. This means that any callback you wish to use, must be defined inside your package.
In order to allow a user of your package to supply a custom callback function, we use the exact same approach as above, but we supply the user's custom handler (which is just a regular Go function) as a parameter that is passed onto the C side as void*
. It is then received by the callbackhandler in our package and called.
Let's use a more advanced example I am currently working with. In this case, we have a C function that performs a pretty heavy task: It reads a list of files from a USB device. This can take a while, so we want our app to be notified of its progress. We can do this by passing in a function pointer that we defined in our program. It simply displays some progress info to the user whenever it gets called. Since it has a well known signature, we can assign it its own type:
type ProgressHandler func(current, total uint64, userdata interface{}) int
This handler takes some progress info (current number of files received and total number of files) along with an interface{} value which can hold anything the user needs it to hold.
Now we need to write the C and Go plumbing to allow us to use this handler. Luckily the C function I wish to call from the library allows us to pass in a userdata struct of type void*
. This means it can hold whatever we want it to hold, no questions asked and we will get it back into the Go world as-is. To make all this work, we do not call the library function from Go directly, but we create a C wrapper for it which we will name goGetFiles()
. It is this wrapper that actually supplies our Go callback to the C library, along with a userdata object.
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"
Note that the goGetFiles()
function does not take any function pointers for callbacks as parameters. Instead, the callback that our user has supplied is packed in a custom struct that holds both that handler and the user's own userdata value. We pass this into goGetFiles()
as the userdata parameter.
// 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 goProgressCBfunc 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 ))}
That's it for our C bindings. The user's code is now very straight forward:
package mainimport ( "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}
This all looks a lot more complicated than it is. The call order has not changed as opposed to our previous example, but we get two extra calls at the end of the chain:
The order is as follows:
foo.GetFiles(....) -> C.goGetFiles(...) -> C.somelib_get_files(..) -> C.goProgressCB(...) -> foo.goProgressCB(...) -> main.myProgress(...)