我相信您的目标是将可调用的Python对象传递给接受的对象
std::function。您需要创建一些C ++代码来实现它,但这相当简单。
从尽可能简单地定义“ accepts_std_function.hpp”开始,以提供一个说明性示例:
#include <functional>#include <string>inline void call_some_std_func(std::function<void(int,const std::string&)> callback) { callback(5,std::string("hello"));}然后,诀窍是创建一个包含
PyObject*和的包装器类
operator()。定义
operator()允许将其转换为
std::function。大部分班级只是口算。“
py_obj_wrapper.hpp”:
#include <Python.h>#include <string>#include "call_obj.h" // cython helper fileclass PyObjWrapper {public: // constructors and destructors mostly do reference counting PyObjWrapper(PyObject* o): held(o) { Py_XINCREF(o); } PyObjWrapper(const PyObjWrapper& rhs): PyObjWrapper(rhs.held) { // C++11 onwards only } PyObjWrapper(PyObjWrapper&& rhs): held(rhs.held) { rhs.held = 0; } // need no-arg constructor to stack allocate in Cython PyObjWrapper(): PyObjWrapper(nullptr) { } ~PyObjWrapper() { Py_XDECREF(held); } PyObjWrapper& operator=(const PyObjWrapper& rhs) { PyObjWrapper tmp = rhs; return (*this = std::move(tmp)); } PyObjWrapper& operator=(PyObjWrapper&& rhs) { held = rhs.held; rhs.held = 0; return *this; } void operator()(int a, const std::string& b) { if (held) { // nullptr check call_obj(held,a,b); // note, no way of checking for errors until you return to Python } }private: PyObject* held;};该文件使用非常短的Cython文件来完成从C ++类型到Python类型的转换。“ call_obj.pyx”:
from libcpp.string cimport stringcdef public void call_obj(obj, int a, const string& b): obj(a,b)
然后,您只需要创建包装这些类型的Cython代码即可。编译该模块并调用
test_func以运行它。(“ simple_version.pyx” :)
cdef extern from "py_obj_wrapper.hpp": cdef cppclass PyObjWrapper: PyObjWrapper() PyObjWrapper(object) # define a constructor that takes a Python object # note - doesn't match c++ signature - that's fine!cdef extern from "accepts_std_func.hpp": void call_some_std_func(PyObjWrapper) except + # here I lie about the signature # because C++ does an automatic conversion to function pointer # for classes that define operator(), but Cython doesn't know thatdef example(a,b): print(a,b)def test_call(): cdef PyObjWrapper f = PyObjWrapper(example) call_some_std_func(f)
上面的版本可以使用,但是在一定程度上受到限制,因为如果您想使用其他
std::function专门化的方法来做,则需要重写其中的一些(并且从C
++到Python类型的转换自然不会适合模板实现)。一种简单的解决方法是使用Boost Python库
object类,该类具有templated
operator()。这是以引入额外的库依赖性为代价的。
首先定义标头“
boost_wrapper.hpp”以简化从
PyObject*到的转换
boost::python::object
#include <boost/python/object.hpp>inline boost::python::object get_as_bpo(PyObject* o) { return boost::python::object(boost::python::handle<>(boost::python::borrowed(o)));}然后,您只需使用Cython代码包装此类(“ boost_version.pyx”)。再说一次
test_func
cdef extern from "boost_wrapper.hpp": cdef cppclass bpo "boost::python::object": # manually set name (it'll conflict with "object" otherwise bpo() bpo get_as_bpo(object)cdef extern from "accepts_std_func.hpp": void call_some_std_func(bpo) except + # again, lie about signaturedef example(a,b): print(a,b)def test_call(): cdef bpo f = get_as_bpo(example) call_some_std_func(f)
一个“ setup.py”
from distutils.core import setup, Extensionfrom Cython.Build import cythonizeextensions = [ Extension("simple_version", # the extension namesources=["simple_version.pyx", "call_obj.pyx" ],language="c++", # generate and compile C++ pre ), Extension("boost_version", # the extension namesources=["boost_version.pyx"],libraries=['boost_python'],language="c++", # generate and compile C++ pre ) ]setup(ext_modules = cythonize(extensions))(最后一种选择是用于
ctypes从可调用的Python生成C函数指针。请参见使用不带gil的类的方法的函数指针(答案的下半部分)和http://osdir.com/ml/python-cython- devel /
2009-10 / msg00202.html。我在这里不做详细介绍。)



