且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

如何在本机回调中使用Cython cdef类成员方法

更新时间:2021-10-10 01:10:15

std :: function 可以接受一系列参数。以最简单的形式,它需要一个函数指针,该函数指针直接映射到其模板类型,而这正是Cython包装器真正要处理的所有内容。对于包装C ++成员函数,通常使用 std :: mem_fun std :: mem_fun_ref 相关类实例的副本或对相关实例的引用(在现代C ++中,您可能还会选择一个lambda函数,但这实际上只是为您提供了相同的选项)。

std::function can take a range of arguments. In its simplest form it takes a function pointer that directly maps to its template type, and this is all that the Cython wrappers for it are really set up to cope with. For wrapping a C++ member function you'd typically use std::mem_fun or std::mem_fun_ref to either take a copy of, or a reference to, the relevant class instance (in modern C++ you might also choose a lambda function, but that really just gives you the same options).

将其转换为Python / Cython,您需要在要调用其成员的对象上保留一个 PyObject * ,并负责处理自己计算的引用。很遗憾,Cython目前无法为您生成包装器,因此您需要自己编写一个支架。基本上,您需要一个可处理的对象来处理引用计数。

Converting this to Python/Cython you need to hold a PyObject* to the object whose member you are calling, and with that comes a responsibility to handle the reference counting yourself. Unfortunately Cython can't currently generate the wrappers for you, and so you need to write a holder yourself. Essentially you need a callable object that handles the reference counting.

先前的答案我展示了创建这种包装器的两种方法(一种是手动的,另一种是通过使用Boost Python来节省精力的,已经实现了非常相似的功能)。我在此处显示的方案适用于与相关签名匹配的任何Python可调用。尽管我用标准的Python模块级函数进行了说明,但是它对于成员函数也同样有效,因为 instance.memberfunction 会生成绑定成员函数-一个可调用的Python可调用函数

In a previous answer I showed two ways of creating this kind of wrapper (one of which was manual, and the second of which saved effort by using Boost Python, which has already implemented something very similar). The scheme I showed there will work for any Python callable matching the relevant signature. Although I illustrated it with a standard Python module level function it would work equally well for a member function since instance.memberfunction generates a bound member function - a Python callable which will work just as well.

对于您的问题,唯一的区别是您使用的是 cdef 成员函数而不是 def 成员函数。看起来应该可以,但不能(最近版本的Cython尝试将其自动转换为可调用的Python,但由于运行时错误而失败)。您可以将lambda函数创建为非常简单的包装器。从速度的角度来看,这比理想情况稍差一些,但具有使用现有代码的优势。

For your problem the only difference is that you are using a cdef member function rather than a def member function. This looks like it should work but doesn't (recent versions of Cython attempt to do an automatic conversion to a Python callable but it fails with a runtime error). You can create a lambda function though as a very simple wrapper. This is slightly less-than-ideal from a speed point of view but has the advantage of working with the existing code.

您需要对较早的答案进行修改如下:

The modifications you will need to make to earlier answer are as follows:


  • 如果您尝试使用手册 PyObjectWrapper 版本,然后更改参数类型以匹配您的签名(即,将 int,string& 更改为 int )。

  • If you are trying to use the manual PyObjectWrapper version then change the argument types to match your signature (i.e. change int, string& to int). You don't have to do this for the boost version.

您用于 void的包装器void register(std :: function< void( int)>回调); 必须为:

cdef extern from "whatever.hpp":
  void register(PyObjWrapper)
  # or
  void register(bpo)

取决于您使用的版本。这是Cython关于签名的谎言,但是由于这两个对象都是可调用的C ++对象,因此它们可以由C ++编译器自动转换为 std :: function

depending on which version you're using. This is lying to Cython about the signature, but since both of these objects are callable C++ objects they are automatically convertable to std::function by the C++ compiler.

调用寄存器作为寄存器(PyObjWrapper(lambda x:self.member_callback(x)) ) register(get_as_bpo(lambda x:self.member_callback(x)))

有可能创建一个更有效的版本,专门针对使用 cdef 函数。它将基于与 PyObjWrapper 非常相似的对象为基础。但是,鉴于我已经编写的代码可以使用,因此我不愿意在没有明确理由的情况下执行此操作。

It would be possible to create a more efficient version specifically targeted at using cdef functions. It would be based around an object that is pretty similar to a PyObjWrapper. However, I'm reluctant to do this without a clear justification given that the code I've already written will work.