如何使用Cython将Python函数作为参数传递给C++函数

6
以下是我的设置: 我有一个C++类需要包装:
// Foo.h
class Foo
{
public:
  typedef int MyType;
  typedef int ArgType1;
  typedef int ArgType2;
  ...
  typedef MyType (*FooFunction) (ArgType1 a, ArgType2 b);
  ...
  void setFooFunction(FooFunction f);

在c++中使用该类的示例:

#include "Foo.h"
...
int fooFun(int a, int b)
{
  if (a > b) return a;
  else return b;
}
...
int main(int argc, char **argv)
{
  ...
  fooObj->setFooFunction(&fooFun);
  ...
}  

Cython封装器:

 # .pyx
 cdef extern from "Foo.h":
   cdef cppclass Foo:
     void setFooFunction(int *) except +

 def bar(fooFun):
   ...
   fooobj.setFooFunction(fooFun)
   ...

我希望能够做到以下事情:

 # python file
 ...
 def pyfun(x, y):
   return x + y
 ...
 def main():
   bar(pyfun)

我不完全熟悉Cython,但我已经尝试做一些魔法,但它没有起作用:

 # .pyx
 cdef extern from "Foo.h":
   cdef cppclass Foo:
     void setFooFunction(int *) except +

 ctypedef int (*myFun) (int, int)

 def bar(fooFun):
   cdef myFun funpointer
   funpointer = (<myFun*><size_t>id(smoothfun))[0]
   ...
   fooobj.setFooFunction(<int*>fooFun)
   ...

这样的事情真的可行吗?

如果您成功完成了这个任务,能否在这里分享一下结果呢?谢谢。 - JC1
1个回答

3
你不能轻易地使用C++函数指针来存储Python函数。C++函数指针只是存储函数代码开始的内存位置(或类似的实现细节),而Python函数是一个完整的Python对象,其中包含一个字典来存储字节码(可能是未编译的Python代码)、文档字符串和其他一些信息。它也不是以机器可以独立运行的形式存在的——它需要一个Python解释器来处理字节码。实际上没有办法将所有这些内容存储在C++函数指针中。
你可以尝试使用C++11的std::function。它可以像函数指针一样使用,并且可以接受任何可调用对象(即定义了operator()的任何东西)。想法是你的类存储一个std::function而不是一个函数指针。
#include <functional> // for std::function

class Foo {
  private:
    std::function<int(int,int)> stored_function;

  public:
    void setFooFunction(std::function<int(int,int)> f) {
      stored_function = f;
    }

    void doSomething() {
      // call it like this
      int result = stored_function(1,2);
    }
};

你可以将存储Python函数的PyObject*作为参数传递给setFooFunction函数,并定义operator()函数来调用Python。如果你不想亲自编写C++类,boost::python::object类(http://www.boost.org/doc/libs/1_58_0/libs/python/doc/v2/object.html#object_operators-spec)拥有所需功能。你可以轻松地从Cython中获取PyObject*。
from cpython.ref cimport PyObject
cdef PyObject* pyob_ptr = <PyObject*>some_function

将其转换为boost c++类也是相当简单的 (http://www.boost.org/doc/libs/1_58_0/libs/python/doc/tutorial/doc/html/python/object.html#python.creating_python_object)。

boost::python::object o(boost::python::handle<>(boost::python::borrowed(pyobj_ptr)));

由于o是可调用的,所以您应该可以直接在std::function中使用它。

Foo foo; // a foo object
foo.setFooFunction(o); // pass it the boost::python::object

(显然这里缺少很多细节,但希望这个广泛的方法会有用!)

我在想,当我创建cdef cppclass的方法并将其传递给此参数时,该如何声明函数。 void map(function<int(int)>)不起作用,因为这实际上是语法错误,而我不确定如何在pxd文件中声明它。 - OakenDuck
Cython使用[]括号作为模板,而不是<>括号。(我想这就是你在问什么?) - DavidW

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