在 cython 中使用函数指针作为模板参数包装 C++ 代码

Pav*_*lin 4 c++ python cython

我试图将以下用 C++ 编写的声明包装在 cython 中:

template<typename T, double (*distance)(const DataPoint&, const DataPoint&)>
class VpTree
{...}
Run Code Online (Sandbox Code Playgroud)

我还在 C++ 中得到了以下定义:

inline double euclidean_distance(const DataPoint &t1, const DataPoint &t2) {...}
Run Code Online (Sandbox Code Playgroud)

我正在尝试将其包装在 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
        ...
Run Code Online (Sandbox Code Playgroud)

并围绕它构建一个包装器:

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

    def __cinit__(self):
        self.tree = VpTree[DataPoint, euclidean_distance]()
Run Code Online (Sandbox Code Playgroud)

不幸的是,这会导致以下错误:

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

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
Run Code Online (Sandbox Code Playgroud)

我怀疑问题出在F定义的部分,并且我尝试了各种方法来代替它,例如,double(*)(DataPoint&, DataPoint&)但这显然会导致语法错误。

ead*_*ead 8

据我所知,Cython 不直接支持非类型模板参数(这就是函数指针)(虽然我可能错过了备忘录),但有一个众所周知的cname-hack可以实现这一目标。

在这里,举一个更简单的例子:

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

int-value 作为模板参数。

现在我们遇到了一个困境:Cython 期望 T 为类型,而 g++(或其他编译器)期望一个整数值 - 这里 cname -hack来拯救我们:

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

def doit():
    return fun[val2]()
Run Code Online (Sandbox Code Playgroud)

Cython 认为val2它是一种类型( 的别名),但在生成的 C++ 代码int中将其替换为( ),因此 c++ 编译器会看到一个整数值(在本例中),正如它所期望的那样。2fun<2>()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]()
Run Code Online (Sandbox Code Playgroud)

如果您不在 Cython 代码中的其他任何地方使用它,实际上根本不必包装“euclidean_distance”。