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

时间:2012-12-22 13:05:31

标签: c++ interop go swig language-interoperability

我正在尝试调用C ++函数:

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

来自Go Code。

我真的希望它只是将Go函数传递给该函数。我知道我可以将它包装成一个类,并使用%feature(“director”)来解决它,但在我的情况下,这不是最佳解决方案。

从我在this page中看到的,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=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

请注意它确实打印了“SUCCESS”,这意味着函数DID运行,即使我把更复杂(和长)的代码放到这个函数中,它确实执行得很好,但它没有回来: (

请让我知道您的想法,以及如何解决这个问题。 我不介意在C ++部分添加一些代码,但我真的希望Go部分看起来“干净”。

谢谢。

1 个答案:

答案 0 :(得分:5)

成功!我有一个有效的解决方案:

我所做的想法是用“director”包装回调,并将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 director),我将拥有这个扩展类的全局实例。因此,调用该实例的Run()将调用Go函数,该函数将接收函数指针。

请注意我的TestFunc现在而不仅仅是运行f(),通过GlobalCallback运行它。通过添加另一个返回指向运行GlobalCallback->运行(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()函数,该函数使用运行指针的Go函数设置GlobalCallback。

就是这样,Go代码就像它一样,并且它有效:)