通过 SWIG 从 C++ 调用 Go 回调函数
Posted
技术标签:
【中文标题】通过 SWIG 从 C++ 调用 Go 回调函数【英文标题】:Calling a Go callback function from C++ through SWIG 【发布时间】:2012-12-22 13:05:30 【问题描述】:我正在尝试调用 C++ 函数:
void TestFunc(void(*f)(void)) f();
来自 Go 代码。
我真的希望我只是将一个 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】:成功!我有一个可行的解决方案:
我所做的想法是用“directors”包装回调,并将 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() 函数,该函数使用运行指针的 Go 函数设置 GlobalCallback。
就是这样,Go 代码保持原样,并且可以运行 :)
【讨论】:
以上是关于通过 SWIG 从 C++ 调用 Go 回调函数的主要内容,如果未能解决你的问题,请参考以下文章
SWIG (Java):如何将带有回调函数的结构从 Android 应用程序传递给 C++?
使用 SWIG 包装对象从 C++ 调用 Python 函数的最简洁方法是啥