用函数指针包装 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&, DataPoint&)
但这显然会导致语法错误。
【问题讨论】:
【参考方案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<2>()
) 中将其替换为 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 中的模板参数的主要内容,如果未能解决你的问题,请参考以下文章