Cython: шаблоны в оболочках класса python

Вопрос

Есть ли способ создать оболочку Python для Cython-wrapped С++ класса с шаблонами? (т.е. делать то, что здесь показано, но с шаблонами: 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 a wish
    cdef Foo[T] *thisptr
    def __cinit__(self, param):
        self.thisptr = new Foo[T](param)
    # ...

Я совершенно уверен, что Cython не поддерживает само по себе, но, возможно, кто-то может подумать об обходном пути. Я не ищу идиоматических или приятных примеров, мне просто интересно, возможно ли это в любом случае.

Ответ 1

Как вы говорите, 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 вы просто "включаете" сгенерированные файлы для каждого класса. Команда "include" в 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, поскольку соответствующие Класс С++ не будет сгенерирован.

Другие параметры

Несколько других вариантов заслуживают краткого рассмотрения. Во-первых, вы можете наследовать Foo<T> из базового класса (скажем FooBase), который не зависит от параметра шаблона. Затем вы завернете FooBase в Cython (создаете конструктор-подобные функции для случаев, о которых вы заботитесь). Это действительно реально, если функции, которые вы хотите вызвать, не имеют аргументов, которые зависят от типа шаблона. Очевидно, что это также связано с изменением кода на С++.

Опция раздела - это посмотреть на другой способ обертывания. Boost Python, безусловно, поддержит это изначально (но имеет свои недостатки). Я думаю, SIP/SWIG также справится (но я не знаю). Вы можете довольно легко смешивать и сопоставлять их с Cython, если это необходимо (путем импорта сгенерированного модуля, содержащего ваши классы шаблонов).