使用SWIG处理以函数作为输入参数的函数

3
考虑以下最简单的例子:我有一个C++函数'myfun',它接受一个数字和另一个函数作为输入,并返回在x处评估的函数:
``` double myfun(double x, double (*f)(double y)) { return f(x); } ```
我将这段代码存储在“test.cpp”中。相应头文件“test.hpp”的代码非常简单:
``` double myfun(double x, double (*f)(double y)); ```
现在,我尝试使用SWIG从Python访问“myfun”。'swig.i'文件如下所示:
``` %module test %{ #include "test.hpp" %} double myfun(double x, double (*f)(double y)); ```
每当我尝试在Python中调用此函数时,例如:
``` from test import myfun myfun(2, lambda y: y**2) ```
我会收到以下错误:
``` in method 'myfun', argument 2 of type 'double (*)(double)' ```
所以我的问题很简单:如何修改“swig.i”文件,以便我可以将任何(适合的Python)函数传递给“myfun”?(这个问题与此帖子如何使用SWIG在Python中包装接受函数指针的c++函数有些相关,但不同之处在于这里我没有在C++中指定输入函数,而是想将其保留为自由的“参数”)。

如果你只需要一个Python解决方案,考虑使用cython。SWIG有(或曾经有)引入不兼容语法更改的倾向。以前的雇主在这方面遇到了巨大的问题。 - Martin Bonner supports Monica
2个回答

3

当编写绑定时,回调函数需要额外处理,而SWIG目前不支持这一点,似乎也没有真正的解决方法。

主要问题在于SWIG是用于从Python(和其他语言)调用C代码,而不是从C代码调用Python。

我们使用SIP代替SWIG,它是一个更为复杂的绑定生成器(但是仅限于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的函数也可以有上下文)。
完整示例可从此处下载。

1
根据文档(强调是我的):
尽管SWIG通常不允许在目标语言中编写回调函数,但使用类型映射和其他高级SWIG功能可以实现此目的。
如果您想更深入地了解该主题,则建议阅读此章节。无论如何,这似乎并不是该工具的旨在重点功能。
您最好能做的可能是获得围绕Python设计的解决方案,但这些解决方案不能与其他目标语言一起使用,但这通常不是Swig用户所期望的。
请注意,仍然可以在C/C++中编写回调函数并将其导出为常量,然后从目标语言中使用它们。有关详细信息,请参见上述我文档。

我可以使用Python特定的解决方案,只是需要让它工作起来,我不打算深入研究SWIG代码。谢谢! - user56643
@user56643 这是 SWIG 的文档,不是代码。;-) - skypjack

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接