考虑下面的最小示例:我有一个C++函数'myfun‘,它以一个数字和另一个函数作为输入,并返回在x中计算的函数:
双倍(双x,双(*f)(双y)) {返回f(x);}
我将此代码存储在'test.cpp‘中。相应的头文件'test.hpp‘中的代码很简单
双倍(双x,双(*f)(双y));
现在,我尝试使用SWIG来访问Python中的“我的乐趣”。“swg.i”文件如下所示:
%模块测试 %{ #包括"test.hpp“ %} 双倍(双x,双(*f)(双y));
每当我试图在Python中调用这个函数时,例如
从测试进口我的 我的乐趣(2,lambda y: y**2)
我收到错误:
在方法“myfun”中,类型为“double(*)(Double)”的参数2
因此,我的问题很简单:如何修改‘swg.i’文件,以便我可以将任何(合适的Python)函数传递给'myfun'?(这个问题在某种程度上与这个post 如何使用SWIG包装c++函数,该函数在python中接受函数指针‘有关,但区别在于这里我没有在C++中指定输入函数,而是希望将它保留为一个自由的’参数‘)。
发布于 2016-11-11 07:02:26
在编写绑定时,回调需要额外的处理,而且这在SWIG中还不支持,而且显然没有真正的解决办法。
要点是SWIG是关于从Python (和其他语言)调用C,而不是从C调用Python。
我们使用的是啜饮,它是一个更复杂的绑定生成器(但它是特定于C++/Python的)。SIP支持回调,甚至支持从C++类继承Python类。
该工具非常强大,是PyQt绑定背后的引擎。
但是请注意,Python中的可调用函数在C++中不仅仅是一个函数,因为它可以包含一个上下文(可以是一个闭包)。在C++代码中,如:
int callF(int (*f)(int, int), int a, int b) {
return f(a, b);
}只能调用一个纯上下文无关的函数,所以传递给它一个一般的Python回调是不可能的,因为没有足够的信息(唯一的解决方案是分配预编译的蹦床或在运行时生成C++代码)。
但是,如果您可以控制双方,那么解决办法是创建一个包含回调的C类,并在Python中派生它。例如:
struct CBack {
virtual int operator()(int x, int y) const {
return x+y;
}
virtual ~CBack() {}
};
int callF(const CBack& f,
int xv, int yv)
{
return f(xv, yv);
}然后从CBack派生一个Python类并传递:
class Add(CBack):
def __call__(self, x, y):
return x + y
class Mul(CBack):
def __call__(self, x, y):
return x * y
print callF(Add(), 3, 7) # will print 10
print callF(Mul(), 3, 7) # will print 21在这种情况下,您还可以传递一个(包装) lambda。
def cback(f):
class PyCBack(CBack):
def __call__(self, x, y):
return f(x, y)
return PyCBack()
print callF(cback(lambda x, y: x*y), 3, 7)换句话说,一个阻抗问题是,用于C++的函数与用于Python的函数不一样(用于Python的函数也可以具有上下文)。
可以从这里下载完整的示例。
发布于 2016-11-11 06:45:44
根据文档 (强调地雷):
虽然SWIG通常不允许用目标语言编写回调函数,但这可以通过使用类型映射和其他高级SWIG功能来实现。
这是建议的章节,如果您想深入讨论这个主题的话。
无论如何,它看起来似乎不是该工具的一个预期的关键特性。
您所能做的最好的就是获得一个围绕Python设计的解决方案,它不会与其他目标语言一起工作,但这通常不是Swig的用户想要的。
请注意,您仍然可以用C/C++编写回调,并将它们作为常量导出,然后从目标语言中使用它们。
有关更多细节,请参阅上述我的文档。
https://stackoverflow.com/questions/40542159
复制相似问题