interop — вызов функции обратного вызова Go из C ++ через SWIG

Я пытаюсь вызвать функцию C ++:

void TestFunc(void(*f)(void)) { f(); }

Из Go Код.

Я бы очень хотел, чтобы я просто передал функцию Go этой функции. Я знаю, что могу обернуть его в класс и решить с помощью% feature («директор»), но в моем случае это не оптимальное решение.

Из того, что я видел в эта страница, указатели для работы в Go должны быть такими же, как в C ++, поэтому я попробовал следующий файл .swig:

%{
#include "test.h"%}

%typemap(gotype) FUNC* "func()"%typemap(in) FUNC* {
$1 = (void(*)(void))$input;
}
%apply FUNC* { void(*)(void) };

%include "test.h"

Я был немного удивлен, что сначала это сработало, но потом заметил, что это не всегда работает :(.

Например, в этом коде Go он работает как положено:

import "fmt"import "test_wrap"
func main() {
b := false
test_wrap.TestFunc(func() { b = true })
fmt.Println(b)  // This actually DOES print "true"!
}

Но в других случаях это не работает. Например, здесь:

import "fmt"import "test_wrap"
func main() {
test_wrap.TestFunc(func() { fmt.Println("SUCCESS") })
fmt.Println("Done")
}

Я на самом деле получаю:

SUCCESS
SIGILL: illegal instruction
PC=0x4c20005d000goroutine 1 [syscall]:
test_wrap._swig_wrap_TestFunc(0x400cb0, 0x400c2a)
base_go_test__wrap_gc.c:33 +0x32
test_wrap.TestFunc(0x400cb0, 0x2)
base_go_test__wrap.go:37 +0x25
main.main()
test.go:8 +0x2a

goroutine 2 [syscall]:
created by runtime.main
go/gc/src/pkg/runtime/proc.c:225
rax     0x0
rbx     0x0
rcx     0x0
rdx     0x8
rdi     0x4c200073050
rsi     0x4c20004c0f0
rbp     0x0
rsp     0x4c20004c100
r8      0x2
r9      0x4b0ae0
r10     0x4f5620
r11     0x4dbb88
r12     0x4f5530
r13     0x7fad5977f9c0
r14     0x0
r15     0x3
rip     0x4c20005d000
rflags  0x10202
cs      0x33
fs      0x0
gs      0x0

Пожалуйста, обратите внимание, что он напечатал «SUCCESS», это означает, что функция DID была запущена, и даже если я добавлю более сложный (и длинный) код в эту функцию, она выполнится идеально, но не вернется :(.

Пожалуйста, дайте мне знать, что вы думаете, и как я могу решить эту проблему.
Я не против добавить какой-то код в часть C ++, но я действительно хочу, чтобы часть Go выглядела «чисто».

Спасибо.

4

Решение

Успех! У меня есть решение, которое работает:

Идея того, что я сделал, заключается в том, чтобы обернуть обратный вызов «директорами» и «вернуть» указатель функции Go обратно в Go, чтобы его можно было запустить в этом контексте.

Приведенное ниже решение не является идеальным, но оно достаточно близко для моих нужд, и с этого момента его довольно легко сделать идеальным.

Файл C ++:

class Callback {
public:
virtual void Run(void(*f)(void)) = 0;
virtual ~Callback() {}
};

Callback* GlobalCallback;

void TestFunc(void(*f)(void)) {
GlobalCallback->Run(f);
}

Я добавил класс Callback, который будет «расширен» в Go (с использованием Swig-директоров), и у меня будет глобальный экземпляр этого расширенного класса. Таким образом, вызов Run () этого экземпляра вызовет функцию Go, которая получит указатель на функцию.

Обратите внимание, что мой TestFunc теперь вместо того, чтобы просто запускать f (), запускает его через GlobalCallback. Это легко исправить, добавив другую функцию, которая возвращает указатель на функцию, которая выполняет GlobalCallback-> Run (f), и передает этот указатель в функцию вместо * f.

Мой файл Swig:

%{
#include "test.h"%}

%module(directors="1") Callback
%feature("director");

%typemap(gotype) FUNC* "func()"%typemap(in) FUNC* {
$1 = (void(*)(void))$input;
}
%apply FUNC* { void(*)(void) };

%include "test.h"
%insert(go_wrapper) %{
type go_callback struct { }

func (c* go_callback) Run(f func()) {
f()
}

func init() {
SetGlobalCallback(NewDirectorCallback(&go_callback{}))
}
%}

Обратите внимание, что я добавил функцию init (), которая устанавливает GlobalCallback с функцией Go, которая запускает указатель.

Вот и все, код Go такой, какой был, и он работает 🙂

5

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]