douou2026 2012-12-22 13:05
浏览 191
已采纳

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

I'm trying to call the C++ function:

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

From Go Code.

I would really want it to be that I just pass a Go function to that function. 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.

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:

%{
#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:

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 :(.

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".

Thank you.

  • 写回答

1条回答 默认 最新

  • douxing9813 2012-12-23 10:02
    关注

    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.

    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:

    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. Thus, calling Run() of that instance, would call a Go function which will receive a function pointer.

    Please notice that my TestFunc now instead of just running f(), runs it through the 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.

    My Swig file:

    %{
    #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.

    That's it, the Go code is as it was, and it works :)

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 用visualstudio2022创建vue项目后无法启动
  • ¥15 x趋于0时tanx-sinx极限可以拆开算吗
  • ¥500 把面具戴到人脸上,请大家贡献智慧
  • ¥15 任意一个散点图自己下载其js脚本文件并做成独立的案例页面,不要作在线的,要离线状态。
  • ¥15 各位 帮我看看如何写代码,打出来的图形要和如下图呈现的一样,急
  • ¥30 c#打开word开启修订并实时显示批注
  • ¥15 如何解决ldsc的这条报错/index error
  • ¥15 VS2022+WDK驱动开发环境
  • ¥30 关于#java#的问题,请各位专家解答!
  • ¥30 vue+element根据数据循环生成多个table,如何实现最后一列 平均分合并