简体   繁体   English

从C ++通过SWIG调用Go回调函数

[英]Calling a Go callback function from C++ through SWIG

I'm trying to call the C++ function: 我正在尝试调用C ++函数:

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

From Go Code. 来自Go Code。

I would really want it to be that I just pass a Go function to that function. 我真的希望它只是将Go函数传递给该函数。 I know that I can wrap it into a class, and solve it using %feature("director"), but that's not the optimal solution in my case. 我知道我可以将它包装到一个类中,并使用%feature(“director”)解决它,但这不是我案例中的最佳解决方案。

From what I saw in this page , the pointers to function in Go, should be the same as in C++, so I tried the following .swig file: 从我在本页中看到的,Go中的函数指针应该与C ++中的相同,所以我尝试了以下.swig文件:

%{
#include "test.h"
%}

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

%include "test.h"

I was quiet surprised that it worked at first, but then noticed that it doesn't always work :(. 我很安静地感到惊讶,它起初工作,但后来注意到它并不总是有效:(。

For example, in this Go code, it works as expected: 例如,在此Go代码中,它按预期工作:

import "fmt"
import "test_wrap"

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

But in other cases, it doesn't work. 但在其他情况下,它不起作用。 For example, here: 例如,这里:

import "fmt"
import "test_wrap"

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

I actually get: 我真的得到:

SUCCESS
SIGILL: illegal instruction
PC=0x4c20005d000


goroutine 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

Please notice that it did print "SUCCESS", this means that the function DID run, and even if I put more complex (and long) code into this function, it does execute perfectly, but it didn't come back :(. 请注意它确实打印了“SUCCESS”,这意味着函数DID运行,即使我将更复杂(和长)的代码放入此函数中,它确实执行完美,但它没有回来:(。

Please let me know what you think, and how I can solve this problem. 请让我知道你的想法,以及我如何解决这个问题。 I don't mind adding some code on the C++ part, but I really want the Go part look "clean". 我不介意在C ++部分添加一些代码,但我真的希望Go部分看起来“干净”。

Thank you. 谢谢。

Success! 成功! I have a solution that works: 我有一个有效的解决方案:

The idea of what I did, is to wrap the callback with "directors", and "return" the Go function pointer back to Go, so it could be run in that context. 我所做的是用“director”包装回调,并将Go函数指针“返回”Go,这样就可以在该上下文中运行。

The solution below is not perfect, but it's close enough for my needs, and it's pretty easy to make it perfect from here on. 下面的解决方案并不完美,但它足够接近我的需求,从现在开始很容易让它变得完美。

The C++ file: C ++文件:

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

Callback* GlobalCallback;

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

I've added a class Callback, which will be "extended" in Go (using Swig directors), and I'll have a global instance of this extended class. 我添加了一个Callback类,它将在Go中“扩展”(使用Swig director),我将拥有这个扩展类的全局实例。 Thus, calling Run() of that instance, would call a Go function which will receive a function pointer. 因此,调用该实例的Run()将调用Go函数,该函数将接收函数指针。

Please notice that my TestFunc now instead of just running f(), runs it through the GlobalCallback. 请注意我的TestFunc现在而不是仅运行f(),通过GlobalCallback运行它。 It's easy to fix by adding another function that returns a pointer to a function that runs GlobalCallback->Run(f), and pass this pointer to the function instead of *f. 通过添加另一个返回指向运行GlobalCallback-> Run(f)的函数的指针的函数很容易修复,并将此指针传递给函数而不是* f。

My Swig file: 我的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{}))                                                                                                     
}
%}

Notice that i've added an init() function that sets the GlobalCallback with a Go function that runs the pointer. 请注意,我添加了一个init()函数,该函数使用运行指针的Go函数设置GlobalCallback。

That's it, the Go code is as it was, and it works :) 就是这样,Go代码就是这样,它的工作原理:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM