SWIG:如何用自定义代码包装C++方法调用?

2 投票
1 回答
958 浏览
提问于 2025-04-18 06:18

我正在尝试使用SWIG的directors功能,从C++调用Python代码。不过,如果代码在另一个线程中运行,我必须获取全局解释器锁(GIL)。我的代码大致是这样的(不包括线程的部分):

struct Callback {
  virtual ~Callback() {}
  virtual void call() {} // will be overwritten in Python
};

struct Foo {
    Callback *c;
    Foo(Callback* c) : c(c) {}
    void doSomething() { c->call(); } // being called from another thread
};

Python:

from demo import *
class MyCallback(Callback):
    def call(*args):
        print("python callback")

c = MyCallback()
f = Foo(c)
f.doSomething()

Swig:

%module(directors="1", threads="1") demo
%feature("director");

%{
#include <test.h>
%}
%thread;
%include <test.h>

我该如何在调用Python回调之前获取GIL呢?

具体来说,SWIG的threads功能会生成一些代码,在调用C++代码时会释放GIL,但当从C++调用Python代码时,它并不会重新获取GIL,所以我想要实现这个功能。

调用的顺序是这样的:Python -> Foo.doSomething -> C++ -> Callback.call -> Python。

1 个回答

2

获取GUIL(全局解释器锁)的过程在Python手册的线程状态和全局解释器锁部分的非Python创建的线程小节中讲得很清楚。不过你提到的问题是如何在SWIG生成的代码中做到这一点。可以这样做,添加到你的SWIG .i文件中:

struct PythonCallback: public Callback
{
  virtual void call() { 
     getGIL();
     lockedCall();
     releaseGIL();
  } 

  virtual void lockedCall() {} // will be overwritten in Python
};

你只需要暴露PythonCallback(而不是Callback)和它的lockedCall方法。如果你愿意,可以通过SWIG的%rename指令把它改名为call。然后在Python中,你可以从PythonCallback继承,并重写call方法(实际上如果你用%rename改名的话,这个方法就是lockedCall)。

撰写回答