简体   繁体   English

使用SWIG作为具有输入参数功能的函数

[英]Using SWIG for functions with functions as input parameter

Consider the following minimal example: I have a C++ function 'myfun' that takes a number and another function as input, and returns the function evaluated at x: 考虑以下最小的例子:我有一个C ++函数'myfun',它接受一个数字和另一个函数作为输入,并返回在x处计算的函数:

double myfun(double x, double (*f)(double y)) 双myfun(双x,双(* f)(双y))
{ return f(x); {return f(x); } }

I store this code in 'test.cpp'. 我将此代码存储在'test.cpp'中。 The code in the corresponding header file 'test.hpp' is simply 相应头文件'test.hpp'中的代码很简单

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

Now, I try to access 'myfun' from Python using SWIG. 现在,我尝试使用SWIG从Python访问'myfun'。 The 'swig.i' file looks as follows: 'swig.i'文件如下所示:

%module test %模块测试
%{ %{
#include "test.hpp" #include“test.hpp”
%} %}
double myfun(double x, double (*f)(double y)); double myfun(double x,double(* f)(double y));

Whenever I try to call this function in Python, eg with 每当我尝试在Python中调用此函数时,例如使用

from test import myfun 从测试导入myfun
myfun(2, lambda y: y**2) myfun(2,lambda y:y ** 2)

I receive the error: 我收到错误:

in method 'myfun', argument 2 of type 'double (*)(double)' 在方法'myfun'中,参数2'double(*)(double)'

So my question is simply: How to modifiy the 'swig.i' file such that I can pass any (suitable Python) function to 'myfun'? 所以我的问题很简单:如何修改'swig.i'文件,以便我可以将任何(合适的Python)函数传递给'myfun'? (The question is somewhat related to this post How to wrap a c++ function which takes in a function pointer in python using SWIG ' but the difference is that here I do not specify the input function in C++, but instead I want to keep it as a free 'parameter'). (这个问题与这篇文章有点相关如何包装一个c ++函数,它使用SWIG接收python中的函数指针 '但不同之处在于我在C ++中没有指定输入函数,而是我想保持它为一个免费的“参数”)。

Callbacks require extra treatment when writing bindings and this is not (yet) supported in SWIG and apparently there are no real workarounds. 在编写绑定时回调需要额外的处理,这在SWIG中尚未得到支持,显然没有真正的解决方法。

The main point is that SWIG is about calling C from Python (and other languages), but not calling Python from C. 重点是SWIG是关于从Python(和其他语言)调用C,而不是从C调用Python。

We are using SIP instead that is a much more sophisticated binding generator (that is however C++/Python specific). 我们使用的是SIP ,它是一个更复杂的绑定生成器(不过是C ++ / Python特有的)。 SIP supports callbacks and even inheriting Python classes from C++ classes. SIP支持回调甚至从C ++类继承Python类。

The tool is pretty powerful and is the engine behind PyQt binding. 该工具非常强大,是PyQt绑定的引擎。

Note however that a callable in Python is more than a function in C++ because it can contain a context (it can be a closure). 但请注意,Python中的可调用函数不仅仅是C ++中的函数,因为它可以包含上下文(它可以是闭包)。 In C++ code like: 在C ++代码中:

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

can only call a pure context-free function so passing it a general Python callback is impossible because there's not enough information (the only solution would be allocating pre-compiled trampolines or generating C++ code at runtime). 只能调用一个纯无上下文的函数,所以传递一般的Python回调是不可能的,因为没有足够的信息(唯一的解决方案是分配预编译的trampolines或在运行时生成C ++代码)。

If you can control both sides however a workaround is to create a C class containing the callback and derive it in Python. 如果你可以控制双方,但是解决方法是创建一个包含回调的C类并在Python中派生它。 For example: 例如:

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);
}

and then deriving a Python class from CBack and passing that: 然后从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

You can in this case also pass a (wrapped) lambda 在这种情况下,你也可以传递一个(包裹的)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)

In other words one impedance problem is that a function for C++ is not the same as a function for Python (a function for Python can also have context). 换句话说,一个阻抗问题是C ++的函数与Python的函数不同(Python的函数也可以有上下文)。

A full example can be downloaded from here . 可以从这里下载完整的示例。

According to the documentation (emphasis mine): 根据文件 (强调我的):

Although SWIG does not normally allow callback functions to be written in the target language , this can be accomplished with the use of typemaps and other advanced SWIG features. 尽管SWIG通常不允许以目标语言编写回调函数 ,但这可以通过使用类型映射和其他高级SWIG功能来实现。

This is the the suggested chapter if you want to go deeper into the topic. 如果您想深入了解该主题, 是建议的章节。
Anyway, it looks like it is not an intended key feature of the tool. 无论如何,它看起来不是该工具的预期关键功能。
The best you can do probably is to get a solution that is designed around Python and won't work with other target languages, but it is not usually what an user of Swig wants from it. 您可以做的最好的事情是获得一个围绕Python设计的解决方案,并且不能与其他目标语言一起使用,但它通常不是Swig的用户想要的。

Note that you can still write your callbacks in C/C++ and export them as a constants, then use them from the target languages. 请注意,您仍然可以在C / C ++中编写回调并将它们作为常量导出,然后从目标语言中使用它们。
See the above me documentation for further details. 有关详细信息,请参阅上面的文档。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM