Convert go local function to C "callback"

I have a c type e.g. typedef int* (*callback)(int, int); and a C function void test(callback c) {c(1,2);}.
I’m wondering how to do something like this:

cb := func(a,b C.int) { fmt.Println("Go callback!", a, b) }
C.test(cb)

Here is some more code: callbacks/problem/main.go at master · gucio321/callbacks · GitHub

Normally, I’d do export but I have to use local (aka lambda) functions in my project.

I’d be greatful for help as I have this issue +/- 2 years and would be awesome to fix it finally…

Let me tell you that you are going down a very dangerous route, which involves slow-downs due to the CGO overhead, potential crashes due to missing function references, and potential memory leaks if not being very careful.

With that disclaimer, I do not think that this will work without some kind of workaround.

Running the code example you have provided, the error is the following: cannot convert myPrivCallback (variable of type func(a _Ctype_int, b _Ctype_int) _Ctype_int) to type _Ctype_callback

When you run C.test(C.callback(C.goCallback)) you refer to a C function that has been previously exported from the go world.

When you try to run C.test(C.callback(myPrivCallback)) you are referring to a go function and not to an exported C function, hence the conversion fails, because this seems to be illegal. This is why you have to type export goCallback to make C.test(C.callback(C.goCallback)) work in the first place.

With that being said, you may workaround the issue by putting your anonymous functions in a global map and refer to them with integers. When assigning your callback you have to put that integer in the C call and you will also have to handle a unique int counter for that purpose, but that should not be too much of an issue. Just be careful to delete unnecessary anonymous functions from your map to avoid memory leaks.

Here an example:

package main

// extern int goCallback(int ci, int a, int b);
// typedef int (*callback)(int, int, int);
//
// static void test(int ci, callback cb) {
//     cb(ci, 1, 2);
// }
import "C"
import "fmt"

var cbMap map[C.int]func(a, b C.int) C.int

//export goCallback
func goCallback(ci, a, b C.int) C.int {
	return cbMap[ci](a, b)
}

func init() {
	cbMap = make(map[C.int]func(a C.int, b C.int) C.int)
}

func main() {
	cbMap[0] = func(a, b C.int) C.int {
		fmt.Println("Go callback called with", a, b)
		return a + b
	}
	defer delete(cbMap, 0)
	C.test(0, C.callback(C.goCallback))

	// Problem:
	// How to do this
	cbMap[1] = func(a, b C.int) C.int {
		fmt.Println("This is a private function called with", a, b)
		return a + b
	}
	defer delete(cbMap, 1)

	C.test(1, C.callback(C.goCallback))

	// can still call the first function because it is not deleted yet
	C.test(0, C.callback(C.goCallback))
}

Not the ideal solution you would like to have, I know. But I still hope that this helps!

Hi @voidptr127 thank you for your code. I know this mechanizm and unfortunately I can’t use it in my case. I’m writing a wyspowy for an external project and they’re is no way I can interact with the type of C.test and C.callback

I see. If you cannot change the function definition then the only workaround I found is to predefine a set amount of callback functions and map them. You could write a script to generate that boilerplate code, but it would work. You somehow need to know the name of the anonymous function at compile time to export them.

Example:

package main

// extern int goCallback0(int a, int b);
// extern int goCallback1(int a, int b);
// extern int goCallback2(int a, int b);
// typedef int (*callback)(int, int);
//
// static void test(callback cb) {
//     cb(1, 2);
// }
import "C"
import (
	"fmt"
	"unsafe"
)

var cbMap map[int]func(a, b C.int) C.int
var callbackPool []unsafe.Pointer

//export goCallback0
func goCallback0(a, b C.int) C.int {
	return cbMap[0](a, b)
}

//export goCallback1
func goCallback1(a, b C.int) C.int {
	return cbMap[1](a, b)
}

//export goCallback2
func goCallback2(a, b C.int) C.int {
	return cbMap[2](a, b)
}

/* ... and so on ... */

func init() {
	cbMap = make(map[int]func(a C.int, b C.int) C.int)
	callbackPool = []unsafe.Pointer{
		C.goCallback0, C.goCallback1, C.goCallback2,
	}
}

func main() {
	cbMap[0] = func(a, b C.int) C.int {
		fmt.Println("Go callback called with", a, b)
		return a + b
	}
	defer delete(cbMap, 0)
	C.test(C.callback(callbackPool[0]))

	cbMap[1] = func(a, b C.int) C.int {
		fmt.Println("This is a private function called with", a, b)
		return a + b
	}
	defer delete(cbMap, 1)
	C.test(C.callback(callbackPool[1]))
}

The callback pool would have to be scripted to a reasonable size (e.g. 256, 512, 1024 etc.) and you would need to handle what to do if your application exceeds the set number of possible callbacks.

Sorry.

Addendum: According to the CGO wiki

Function pointer callbacks
C code can call exported Go functions with their explicit name. But if a C-program wants a function pointer, a gateway function has to be written. This is because we can’t take the address of a Go function and give that to C-code since the cgo tool will generate a stub in C that should be called. The following example shows how to integrate with C code wanting a function pointer of a give type.

Maybe the wiki gives you what you are looking for then?

import (
    "fmt"
    "runtime/cgo"
)

/*
##include <stdint.h>

extern void go_callback_int(uintptr_t h, int p1);
static inline void CallMyFunction(uintptr_t h) {
    go_callback_int(h, 5);
}
*/
import "C"

//export go_callback_int
func go_callback_int(h C.uintptr_t, p1 C.int) {
    fn := cgo.Handle(h).Value().(func(C.int))
    fn(p1)
}

func MyCallback(x C.int) {
    fmt.Println("callback with", x)
}

func main() {
    h := cgo.NewHandle(MyCallback)
    C.CallMyFunction(C.uintptr_t(h))
    h.Delete()
}

That may work, thank you!

Is there any chance to make a proposal to Go to implement this somehow?

Edit: by ‘implement this’ I mean implement this somehow else e.g. add // export for lancer
Edit 2: by ‘that may work’ i meant the code with maps. I can’t see how the code from Dunkin MSI solve this…

I think laud:
Afaik during compilation anonymous functions becomes separated functions that shares enviroument with it’s parent. Maybe technically exporting them will not be a big deal (local environment could be treated in a range way global variables are for global functions…)