LINUX.ORG.RU

Как протащить указатель на функцию из Go в сишную разделяемую библиотеку?

 ,


2

3

Юзкейс: К основной программе на Go линкуется сишная разделяемая библиотека, которая будет вызывать колбэк реализованный в Go. Как реализовать сабж?

main.go:

package main

/*
#cgo CFLAGS: -I.
#cgo LDFLAGS: -L. mylib.so

void mylib_init(void *log_f);
void mylib_shutdown();
*/
import "C"

import (
        "log"
        "time"
	"unsafe"
)

func main() {
	C.mylib_init((*C.void)(unsafe.Pointer(logMessage)))
	{
		log.Debug("Serving...")

		time.Sleep(5 * time.Second)
	}
	C.mylib_shutdown()
}

// export logMessage
func logMessage(level C.int, message *C.char) {
	switch level {
	case 0:
		log.Error(C.GoString(message))
		break
	case 1:
		log.Warn(C.GoString(message))
		break
	case 2:
		log.Info(C.GoString(message))
		break
	case 3:
		log.Debug(C.GoString(message))
		break
	default:
	}
}

Код не компилируется. Пробовал другие варианты, но тоже не выходит. В гугле внятного ответа не нашел.

./main.go:155: cannot use (*C.void)(unsafe.Pointer(logMessage)) (type *C.void) as type unsafe.Pointer in argument to func literal
./main.go:155: cannot convert logMessage (type func(C.int, *C.char)) to type unsafe.Pointer

Спасибо.

2 года назад в go сделали очень резкое движение с указателями: куча C-биндинг-библиотек было сломано с выходом 1.6. Вот примеры:

Баг-репорт с объяснениями: cmd/cgo: specify rules for passing pointers between Go and C

gag ★★★★★ ()

If Go code passes a Go pointer to a C function, the C function must return. While there are no documented time limits, a C function that simply blocks holding a Go pointer while other goroutines are running may eventually cause the program to run out of memory and fail.

Прикольно

anonymous ()
Ответ на: комментарий от joy4eg

не нашел своего случая для колбэков с динамической линковкой.

Мне тупо нужно протащить указатель на гошную функцию в сишную dll-ку.

Oxdeadbeef ★★★ ()
Последнее исправление: Oxdeadbeef (всего исправлений: 1)
Ответ на: комментарий от Oxdeadbeef

ЛОЛ, решилось:

package main

/*
#cgo CFLAGS: -I.
#cgo LDFLAGS: -L. mylib.so

void mylib_init(void *log_f);
void mylib_shutdown();
*/
import "C"

import (
        "log"
        "time"
	"unsafe"
)

func main() {
        logMsg := &logMessage

	C.mylib_init(unsafe.Pointer(logMsg))
	{
		log.Debug("Serving...")

		time.Sleep(5 * time.Second)
	}
	C.mylib_shutdown()
}

// export logMessage
func logMessage(level C.int, message *C.char) {
	switch level {
	case 0:
		log.Error(C.GoString(message))
		break
	case 1:
		log.Warn(C.GoString(message))
		break
	case 2:
		log.Info(C.GoString(message))
		break
	case 3:
		log.Debug(C.GoString(message))
		break
	default:
	}
}
Oxdeadbeef ★★★ ()
Ответ на: комментарий от Oxdeadbeef

Лучше так:

/*
#cgo CFLAGS: -I.
#cgo LDFLAGS: -L. mylib.so

void mylib_init(void *log_f);
void mylib_shutdown();

extern void logMessage(int, char*);
static inline void realInit()
{
    mylib_init(logMessage);
}

*/
import "C"

import (
        "log"
        "time"
        "unsafe"
)

func main() {
        C.realInit()
        {
                log.Debug("Serving...")

                time.Sleep(5 * time.Second)
        }                                                                                                                                                                                                   
        C.mylib_shutdown()                                                                                                                                                                                  
} 
pftBest ★★★★ ()
Ответ на: комментарий от Oxdeadbeef

Я ее не скопировал чтобы сэкономить размер комментария

pftBest ★★★★ ()
Ответ на: комментарий от Oxdeadbeef

Хотя наверное стоило скопировать, потому что у тебя там ошибка, надо вместо

// export logMessage

писать

//export logMessage

иначе не скомпилится

pftBest ★★★★ ()
Ответ на: комментарий от Oxdeadbeef

Ты сам выбрал этот язык, нечего жаловаться.

У нас в расте все красиво:

use std::ffi::CStr;
use std::os::raw::{c_char, c_int};

type LogFn = extern "C" fn(_: c_int, _: *const c_char);

#[link(name = "my")]
extern "C" {
    fn mylib_init(f: LogFn);
    fn mylib_shutdown();
}

fn main() {
    unsafe { mylib_init(log_message) };

    std::thread::sleep_ms(1000);

    unsafe { mylib_shutdown() };
}

extern "C" fn log_message(level: c_int, message: *const c_char) {
    let msg = unsafe { CStr::from_ptr(message) };
    println!("[{}] {}", level, msg.to_string_lossy());
}

pftBest ★★★★ ()
Ответ на: комментарий от pftBest

Ты сам выбрал этот язык

Да, я его выбрал для обвязки ядра, которое не на нем конечно. В сабже есть миллион библиотек для интеграции с современными «технологиями».

Oxdeadbeef ★★★ ()
Ответ на: комментарий от pftBest

красиво

Не сказал бы. Выглядит как оррыжка александреску. А код то делает простую по-сути вещь.

anonymous ()

если в библиотеке был бы не void * то unsafe ненужен

lberserq ()
Ответ на: комментарий от pftBest

У нас в расте все красиво

Какой ужас :-( Теперь понятно, почему на Расте ничего кроме пары-тройки ПриветМиров то и не написано :-( Ждем следующего «новомодного» язычка :-(

anonymous ()
Ответ на: комментарий от ritsufag

Писать код в комментариях это по-вашему не мусор?

pftBest ★★★★ ()
Ответ на: комментарий от Virtuos86

Борщелисп как раз сидит в той динамической библиотеке и делает основную логику.

Oxdeadbeef ★★★ ()

В го, кстати, можно опускать оператор break в switch. Если нужно поведеление как в си, есть оператор fallthrought

nikolnik ★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.