Передача метода 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 шт):

Автор решения: Pak Uula

Функции 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
→ Ссылка