python - Cython:python 类包装器中的模板

标签 python c++ templates cython

问题

有没有办法用模板为 Cython 包装的 C++ 类创建 Python 包装器? (即完全按照此处显示的内容进行操作,但使用模板:http://docs.cython.org/src/userguide/wrapping_CPlusPlus.html#create-cython-wrapper-class)。

我知道融合类型解决方法 ( https://groups.google.com/forum/#!topic/cython-users/qQpMo3hGQqI ),但这不允许您实例化像 vector<vector<int>> 这样的类: 毫不奇怪,融合类型没有递归的概念。

改写

我想实现的是包装类,例如:

cdef extern from "header.h":
    cdef cppclass Foo[T]:
        Foo(T param)
        # ...

创建一个简单的 Python 包装器:

cdef class PyFoo[T]:  # I know the '[T]' can't be here, it's a wish
    cdef Foo[T] *thisptr
    def __cinit__(self, param):
        self.thisptr = new Foo[T](param)
    # ...

我很确定 Cython 本身不支持它,但也许有人可以想出一个解决方法。我不是在寻找惯用的或好的例子,我只是想知道这是否可能以任何方式。

最佳答案

正如您所说,Cython 并不真正支持这一点。

我认为到目前为止最简单的方法就是使用字符串替换手动生成一堆 Cython 文件。从“foowrapper.pxi.src”文件开始(名称随心所欲...):

cdef class PyFoo_{T}:
  cdef Foo[{T}] *thisptr
  def __cinit__(self, param):
    self.thisptr = new Foo[{T}](param)
    # etc

接下来,通过一个简单的程序(也可以是 Python)运行它以加载文件、进行字符串替换,然后以新名称再次保存文件。关键行只是:

output = code.format(T=T) # where T is a string with a C++ class name 
              # e.g. "int" or "std::vector<double>"

(显然有一些与加载和保存相关的代码我出于懒惰而跳过了)

然后,在您的 Cython 文件中,您只需“包含”为每个类生成的文件。 Cython 中的“include”命令是文字文本包含(类似于 C 预处理器)并且需要一个 .pxi 文件:

cdef extern from "header.h":
    cdef cppclass Foo[T]:
        Foo(T param)
        # ...

include "foowrapper_int.pxi"
include "foowrapper_vectordouble.pxi
# etc

你必须在编译时选择要生成的类,但这是不可避免的(模板是编译时的特性),所以你永远无法从 Python 脚本环境中动态生成它们,因为相应的不会生成 C++ 类。

其他选项

其他几个选项值得简要考虑。

  1. 你可以继承Foo<T>来自不依赖于模板参数的基类(例如 FooBase )。然后你包装 FooBase在 Cython 中(为您关心的情况生成类似构造函数的函数)。只有当您要调用的函数没有依赖于模板类型的参数时,这才真正可行。显然这也涉及更改 C++ 代码。

    std::vector 为例- 类。它的许多成员不依赖于模板类型,因此可以存在于一个公共(public)基础中(可能作为纯虚函数?)。 Cython cdef extern可能看起来像:

    cdef extern from "somewhere.h":
        cdef cppclass VectorBase:
            int size()
            void pop_back()
        cdef cppclass Vector[T](VectorBase):
            void push_back(T)
    

    然后您可以定义一个基本 Python 类来包装它

    cdef class PyVectorBase:
        cdef VectorBase* vb
        def size(self):
            return self.vb.size()
        def pop_back(self):
            self.vb.pop_back()
    

    函数的特定派生 Python 类取决于类型。

    cdef class PyVectorDouble(PyVectorBase):
        def __cinit__(self):
            self.vb = new Vector[double]()
        def push_back(self, value):
            cdef Vector[double]* vd = <Vector[double]*>(self.vb)  # cast is OK because we constructed it...
            vd.push_back(value)
    

    根据有多少“模板相关”参数,这可以节省大量重复。

  2. 看看不同的包装方式。 Boost Python 肯定会原生支持这一点(但有其自身的缺点)。我想 SIP/SWIG 也能应付(但我不知道)。如有必要,您可以相当干净地将它们与 Cython 混合搭配(通过导入包含模板类的生成模块)。

关于python - Cython:python 类包装器中的模板,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31436593/

相关文章:

c++ - 模板错误 -- C++

c++ - 针对特定类型的模板功能的特化

c++ - 整数数组与 C++ 中的整数指针

python - HTML 输出问题

python - 如何删除多行 NumPy 数组?

python - 在 sqlalchemy 中设置 server_default 失败

c++ - INT_MIN 的用途

c++ - gdb 中的递归 sizeof?

c++ - 可变参数模板 - 模棱两可的调用

python - 模型表单中的 Django M2M 字段?