问题
有没有一种方法可以用模板为Cython包装的C++类创建Python包装器?(即,完全按照此处显示的操作,但使用模板:http://docs.cython.org/src/userguide/wrapping_CPlusPlus.html#create-cython包装器类(。
我知道融合类型的解决方法(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++类。
其他选项
其他几个选择值得简要考虑。
您可以从不依赖于模板参数的基类(比如
FooBase
(继承Foo<T>
。然后将FooBase
封装在Cython中(为您关心的情况生成类似构造函数的函数(。只有当要调用的函数没有依赖于模板类型的参数时,这才是真正可行的。显然,这也涉及到更改C++代码。以一个类似
std::vector
的类为例。它的许多成员不依赖于模板类型,因此可以存在于一个公共库中(可能是作为纯虚拟函数?(。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)
取决于多少";依赖于模板的";这些参数可以节省大量重复。
看看另一种包装方式。BoostPython肯定会在本机上支持这一点(但也有其自身的缺点(。我想SIP/SIG也能应付(但我不知道(。如果需要(通过导入包含模板类的生成模块(,您可以非常干净地将它们与Cython混合和匹配。