如何使用 Cython 将 python 函数作为参数传递给 C++ 函数
How to pass python function as an argument to c++ function using Cython
这是我的设置:
我有下一个要包装的 c++ class:
// 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++ 中使用此 class 的示例:
#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)
...
甚至可以做这样的事情吗?
你不能轻易做到:C++ 函数指针只存储内存中该函数代码开始的位置(或类似的东西,特定于实现),而 Python 函数是一个完整的 Python 对象和存储字节码的字典(可能是未编译的 Python 代码)、文档字符串和其他一些位。它也不是机器可以 运行 自己的形式——它需要一个 Python 解释器来处理字节码。确实没有办法将所有这些存储在 C++ 函数指针中。
您可以使用 C++11 std::function
。这可以像函数指针一样使用,并且可以接受任何可调用对象(任何定义 operator()
的对象)。这个想法是你的 class 存储一个 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);
}
};
然后您传递 setFooFunction
存储 PyObject*(Python 函数)的 C++ class,并定义 operator()
来调用 Python.
如果您不想自己编写 C++ class,boost::python::object
class (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++ class 也相当简单 (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
(显然这里遗漏了很多细节,但希望广泛的方法会有用!)
这是我的设置: 我有下一个要包装的 c++ class:
// 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++ 中使用此 class 的示例:
#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)
...
甚至可以做这样的事情吗?
你不能轻易做到:C++ 函数指针只存储内存中该函数代码开始的位置(或类似的东西,特定于实现),而 Python 函数是一个完整的 Python 对象和存储字节码的字典(可能是未编译的 Python 代码)、文档字符串和其他一些位。它也不是机器可以 运行 自己的形式——它需要一个 Python 解释器来处理字节码。确实没有办法将所有这些存储在 C++ 函数指针中。
您可以使用 C++11 std::function
。这可以像函数指针一样使用,并且可以接受任何可调用对象(任何定义 operator()
的对象)。这个想法是你的 class 存储一个 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);
}
};
然后您传递 setFooFunction
存储 PyObject*(Python 函数)的 C++ class,并定义 operator()
来调用 Python.
如果您不想自己编写 C++ class,boost::python::object
class (http://www.boost.org/doc/libs/1_58_0/libs/python/doc/v2/object.html#object_operators-spec) 具有您需要的功能。您可以轻松地从 Cython
from cpython.ref cimport PyObject
cdef PyObject* pyob_ptr = <PyObject*>some_function
并将其转换为 boost c++ class 也相当简单 (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
(显然这里遗漏了很多细节,但希望广泛的方法会有用!)