当前位置: 首页 > 面试题库 >

从Cython传递闭包到C ++

江正德
2023-03-14
问题内容

我有一个接受回调的C ++函数,如下所示:

void func(std::function<void(A, B)> callback) { ... }

我想通过给它一个闭包从Cython调用此函数,即,如果我从C ++调用它,我会用lambda来完成。如果这是一个C函数,它将有一些额外的void*参数:

typedef void(*callback_t)(int, int, void*);

void func(callback_t callback, void *user_data) {
    callback(1, 2, user_data);
}

然后我只想通过PyObject*user_data(有更详细的在这里例子)。

有没有办法以C ++的方式做更多的事情,而不必诉诸显式的user_data


问题答案:

我相信您的目标是将可调用的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 file

class 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 string

cdef 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 that


def 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 signature

def 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, Extension
from Cython.Build import cythonize

extensions = [
    Extension(
           "simple_version",                       # the extension name
           sources=["simple_version.pyx", "call_obj.pyx" ],
           language="c++",                        # generate and compile C++ code
      ),
    Extension(
           "boost_version",                       # the extension name
           sources=["boost_version.pyx"],
           libraries=['boost_python'],
           language="c++",                        # generate and compile C++ code
      )
    ]

setup(ext_modules = cythonize(extensions))

(最后一种选择是用于ctypes从可调用的Python生成C函数指针。请参见使用不带gil的类的方法的函数指针(答案的下半部分)和http://osdir.com/ml/python-cython- devel /
2009-10 / msg00202.html。我在这里不做详细介绍。)



 类似资料:
  • 本文向大家介绍cython 包装DLL:从C ++到Cython到Python,包括了cython 包装DLL:从C ++到Cython到Python的使用技巧和注意事项,需要的朋友参考一下 示例 这展示了一个用Cython包装C ++ dll的简单例子。它将涵盖以下主要步骤: 使用Visual Studio使用C ++创建示例DLL。 用Cython包裹DLL,以便可以在Python中调用它。

  • 问题内容: 我试图了解Go在创建带有参数的匿名函数与将该函数用作闭包之间的区别。这是区别的一个例子。 带参数: 作为关闭: 我的问题是,第一种形式何时比第二种更好?您是否会为此类事情使用参数?我唯一看到第一种形式有用的是从另一个函数返回a时。 问题答案: 使用闭包与使用函数参数之间的区别在于共享同一变量与获取值的副本有关。请考虑下面的两个示例。 在 Closure中, 所有函数调用都将使用中存储的

  • 我试图创建一个JobGenerator类,它将传递一个构建步骤到调用实例。我遇到一个问题,如果我得到这个错误,当我试图运行这个: 哈德逊。远程处理。ProxyException:groovy。lang.MissingMethodException:没有方法org的签名。詹金西。插件。工作流程。cps。2。build()适用于参数类型:(java.util.LinkedHashMap)值:[[job

  • 问题内容: 我正在尝试在互联网连接恢复且变量为true 时调用一个函数。这是我的代码: 我的问题是这些行: 导致错误:“ ” 我不确定如何检查状态并调用监视Internet连接变化的闭包。如何解决此问题,或者我应该使用完全不同的解决方法? 问题答案: 类似于如何将实例方法用作仅使用func或立即数闭包的函数的回调中一样,您必须转换 为void指针,将其存储在上下文中,然后将其转换回闭包中的对象指针

  • 我最近开始学习Swift,遇到了一个关于闭包的问题。我试图将开车的参数传递给func travel并收到错误消息:无法将类型'()'的值转换为预期的参数类型'()- 有人能善意地建议吗?赞赏!

  • 问题内容: 我有一个实现RecyclerView.OnItemTouchListener的片段。如何 仅 将ReclickerView 事件和单击和Long-click运动事件 传递给GestureDetectorCompat。这就是说,我只想处理单击和长按,其余事件应该由RecyclerView处理,因为它通常会发生。我该如何设置? 问题答案: 您必须在方法中进行初始化: 是您自己的内部类扩展(