问题
有没有办法用模板为 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++ 类。
其他选项
其他几个选项值得简要考虑。
你可以继承
Foo<T>
来自不依赖于模板参数的基类(例如FooBase
)。然后你包装FooBase
在 Cython 中(为您关心的情况生成类似构造函数的函数)。只有当您要调用的函数没有依赖于模板类型的参数时,这才真正可行。显然这也涉及更改 C++ 代码。以
std::vector
为例- 类。它的许多成员不依赖于模板类型,因此可以存在于一个公共(public)基础中(可能作为纯虚函数?)。 Cythoncdef 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)
根据有多少“模板相关”参数,这可以节省大量重复。
看看不同的包装方式。 Boost Python 肯定会原生支持这一点(但有其自身的缺点)。我想 SIP/SWIG 也能应付(但我不知道)。如有必要,您可以相当干净地将它们与 Cython 混合搭配(通过导入包含模板类的生成模块)。
关于python - Cython:python 类包装器中的模板,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31436593/