对函数使用SWIG作为输入参数

Using SWIG for functions with functions as input parameter

本文关键字:输入 参数 SWIG 函数      更新时间:2023-10-16

考虑以下最小示例:我有一个c++函数'myfun',它接受一个数字和另一个函数作为输入,并返回在x处求值的函数:

double myfun(double x, double (*f)(double y))
{返回f (x);}

我将这段代码存储在'test.cpp'中。对应的头文件'test.hpp'中的代码只是

double myfun(double x, double (*f)(double y));

现在,我尝试使用SWIG从Python访问'myfun'。"痛饮。我的文件如下所示:

%module test
% {
#include "test.hpp"
%}
Double myfun(Double x, Double (*f)(Double y));

每当我尝试在Python中调用这个函数时,例如使用

from test import myfun
Myfun (2, lambda y: y**2)

我收到错误:

方法'myfun',参数2类型'double (*)(double)'

所以我的问题很简单:如何修改"swig"。i'的文件,这样我可以传递任何(合适的Python)函数'myfun'?(这个问题在某种程度上与这篇文章有关,如何包装一个c++函数,它在python中使用SWIG',但不同的是,这里我没有指定c++中的输入函数,而是我想把它作为一个自由的'参数')。

回调在编写绑定时需要额外的处理,这在SWIG中还不支持,显然没有真正的解决方案。

要点是SWIG是关于从Python(和其他语言)调用C,而不是从C调用Python。

我们使用SIP,它是一个更复杂的绑定生成器(不过是c++/Python特定的)。SIP支持回调,甚至可以从c++类继承Python类。

这个工具非常强大,是PyQt绑定背后的引擎。

请注意,Python中的可调用对象在c++中不仅仅是函数,因为它可以包含上下文(它可以是闭包)。在c++代码中:

int callF(int (*f)(int, int), int a, int b) {
    return f(a, b);
}

只能调用一个纯上下文无关的函数,因此传递给它一个通用的Python回调是不可能的,因为没有足够的信息(唯一的解决方案是分配预编译的trampolines或在运行时生成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的函数也可以有上下文)。

完整的示例可以从这里下载

根据文档(重点是我的):

尽管SWIG通常不允许用目标语言编写回调函数,但这可以通过使用类型映射和其他高级SWIG特性来完成。

如果你想更深入地了解这个主题,建议你读这一章。
无论如何,看起来它并不是该工具的一个预期的关键特性。您能做的最好的事情可能是获得一个围绕Python设计的解决方案,并且不能与其他目标语言一起工作,但这通常不是Swig用户想要的。

请注意,您仍然可以在C/c++中编写回调并将其导出为常量,然后在目标语言中使用它们。