Cython:python类包装器中的模板



问题

有没有一种方法可以用模板为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++类。

其他选项

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

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

    以一个类似std::vector的类为例。它的许多成员不依赖于模板类型,因此可以存在于一个公共库中(可能是作为纯虚拟函数?(。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. 看看另一种包装方式。BoostPython肯定会在本机上支持这一点(但也有其自身的缺点(。我想SIP/SIG也能应付(但我不知道(。如果需要(通过导入包含模板类的生成模块(,您可以非常干净地将它们与Cython混合和匹配。

相关内容

  • 没有找到相关文章

最新更新