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

Posted

技术标签:

【中文标题】用函数指针包装 C++ 代码作为 cython 中的模板参数【英文标题】:Wrapping C++ code with function pointer as template parameter in cython 【发布时间】:2018-12-02 17:45:26 【问题描述】:

我正在尝试在 cython 中包装以下用 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&amp;, DataPoint&amp;) 但这显然会导致语法错误。

【问题讨论】:

【参考方案1】:

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

这里有一个更简单的例子:

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

int-value 作为模板参数。

现在我们进退两难了:Cython 期望 T 是类型,而 g++(或其他编译器)期望整数值 - cname-hack 来拯救我们:

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

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

Cython 认为 val2 是一种类型(int 的别名),但在生成的 c++ 代码 (fun&lt;2&gt;()) 中将其替换为 2,因此 c++ 编译器会看到一个整数值 (@987654328 @ 在这种情况下),正如它所期望的那样。


对于您的情况,这意味着添加:

%%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]()

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

【讨论】:

哇,这是一个巧妙的技巧。我以前没有遇到过。非常感谢!

以上是关于用函数指针包装 C++ 代码作为 cython 中的模板参数的主要内容,如果未能解决你的问题,请参考以下文章

Cython 中的 C++ 指针

Python Cookbook(第3版)中文版:15.10 用Cython包装C代码

cython wrap cpp 结构和函数,参数为结构数组

在 cython 中处理默认参数

在 Cython 中包装自定义类型 C++ 指针

从 C++ 函数 Cython 返回包含 PyObject 的复杂对象