Обертывание кода C ++ с указателем на функцию в качестве параметра шаблона в Cython - PullRequest
0 голосов
/ 02 декабря 2018

Я пытаюсь заключить в C ++ следующее объявление, написанное на C ++:

template<typename T, double (*distance)(const DataPoint&, const DataPoint&)>
class VpTree
{...}

У меня также есть следующее определение на C ++:

inline double euclidean_distance(const DataPoint &t1, const DataPoint &t2) {...}

, и япытаясь обернуть это в Cython.Вот что мне удалось придумать, следуя документации:

cdef extern from "vptree.h":
    # declaration of DataPoint omitted here

    cdef inline double euclidean_distance(DataPoint&, DataPoint&)

    cdef cppclass VpTree[T, F]:  # F is almost certainly wrong
        ...

и построить оболочку вокруг этого:

cdef class VPTree:
    cdef VpTree[DataPoint, euclidean_distance] tree

    def __cinit__(self):
        self.tree = VpTree[DataPoint, euclidean_distance]()

К сожалению, это приводит к следующим ошибкам:

------------------------------------------------------------

cdef class VPTree:
    cdef VpTree[DataPoint, euclidean_distance] tree
                                            ^
------------------------------------------------------------

unknown type in template argument

------------------------------------------------------------

cdef class VPTree:
    cdef VpTree[DataPoint, euclidean_distance] tree

    def __cinit__(self):
        self.tree = VpTree[DataPoint, euclidean_distance]()
                                     ^
------------------------------------------------------------

unknown type in template argument

Я подозреваю, что проблема заключается в F части определения, и вместо этого я пробовал разные вещи, например double(*)(DataPoint&, DataPoint&), но это, очевидно, приводит к синтаксической ошибке.

1 Ответ

0 голосов
/ 03 декабря 2018

Насколько я знаю, Cython напрямую не поддерживает нетипизированные параметры шаблона (именно таков указатель на функцию) (хотя я мог пропустить заметку, хотя), но есть хорошо известный cname-hack для достижения цели.

Здесь приведен гораздо более простой пример:

%%cython --cplus            
cdef extern from *:
    """
    template<int val>
    int fun(){return val;}
    """
    int fun[T]()

, то есть значение int в качестве параметра шаблона.

Теперь у нас возникла дилемма: Cython ожидает, что T будет типом, а g ++ (или другие компиляторы) ожидает целочисленное значение - здесь на помощь приходит cname-hack :

%%cython --cplus            
...
cdef extern from *:
    ctypedef int val2 "2" 

def doit():
    return fun[val2]()

Cython считает val2 типом (псевдоним для int), но заменяет его на 2 в результирующем коде c ++ (fun<2>()), таким образом, c ++ - компилятор видит целочисленное значение (2 в данном случае), как и ожидалось.


Для вашего случая это означает добавление:

%%cython --cplus            
...
cdef extern from *:
    ctypedef int euclidean_distance_t "euclidean_distance" 

cdef class VPTree:
     cdef VpTree[DataPoint, euclidean_distance_t] tree

     def __cinit__(self):
         self.tree = VpTree[DataPoint, euclidean_distance_t]()

На самом деле вам вообще не нужно переносить "euclidean_distance", если вы не используете его где-либо еще в коде Cython.

...