Передача метода go как аргумент в метод СИ
Использую CGO и столкнулся с проблемой, вот пример кода (только суть):
package main
import "C"
import (
"fmt"
)
func main() {
card = C.Register_Card(C.PCI_7432, cardNumber)
C.DIO_SetDualInterrupt(C.U16(card), C.INT1_EXT_SIGNAL, C.INT2_DISABLE, interrupt, nil)
}
func interrupt(signal int) {
fmt.Printf("signal is %v \n", signal)
}
То получаю ошибку:
cannot use interrupt (value of type func(signal int)) as *[0]byte value in argument to (_Cfunc_DIO_SetDualInterrupt)
Сам метод из .h библиотеки выглядит так:
I16 DIO_SetDualInterrupt (U16 CardNumber, I16 Int1Mode, I16 Int2Mode, void (*event1_handler)(int), void (*event2_handler)(int));
Вопрос 1: Почему указатель на функцию C становится *[0]byte ?! Вопрос 2: Отчасти понятно почему нельзя передать go метод в си, но как обойти подобное?
Ответы (1 шт):
Функции Go нельзя передавать как указатели на функции. Поэтому вместо функции вам нужно передавать некий абстрактный хэндлер, который нужно будет превращать в функцию на Го-шной стороне. Соответственно, сишная сторона вызывает не функцию напрямую, а некий диспетчер, которому передаёт хэндлер в качестве идентификатора функции.
Вот пример.
Сишная сторона регистрирует хэндлер и при необходимости вызывает гошного диспетчера с аргументом для обработчика
#include <stdint.h>
extern void GoCallbackDispatcher(uintptr_t h, int arg);
uintptr_t handler = 0;
void set_handler(uintptr_t h) { handler = h; }
void call_go(int arg) {
GoCallbackDispatcher(handler, arg);
}
void test_it(int arg) {
call_go(arg);
}
Гошная сторона
package main
/*
#include <stdint.h>
void set_handler(uintptr_t h);
void call_go(int arg);
void test_it(int arg);
*/
import "C"
import (
"fmt"
"runtime/cgo"
)
//export GoCallbackDispatcher
func GoCallbackDispatcher(h C.uintptr_t, c_arg C.int) {
if h == 0 {
panic("handler was not set")
}
handle := cgo.Handle(h)
arg := int(c_arg)
hfunc, ok := handle.Value().(func(int))
if !ok {
panic(fmt.Errorf("invalid handler: expected `func(int)`, got `%T`", handle.Value()))
}
hfunc(arg)
}
func main() {
h := cgo.NewHandle(PrintInt)
C.set_handler(C.uintptr_t(h))
C.call_go(12345)
}
func PrintInt(arg int) {
fmt.Println("PrintInt: ", arg)
}
В результате программа печатает
PrintInt: 12345