在 Cython 中优雅地包装模板化对象

Wrapping templated objects elegantly in Cython

本文关键字:对象 包装 Cython      更新时间:2023-10-16

这类似于这个问题,但它从未得到任何解决方案,我至少有一个解决这个问题的方法,尽管它很不优雅。

我正在尝试包装一个模板化类,Point<_T,__Scale> ,其中 _T=int,float...__Scale 是一个 int。现在,编译器将为使用的每个模板值生成一个单独的类,但这些类没有任何关系。但是,这些类共享它们的所有方法,主要是!=<>*&/|和getter的运算符重载。

在 Cython 中,我能够包装Point<_T,__Scale>的唯一方法是为每个变体提供一个 cdef 类。它可以工作,但会导致大量复制粘贴的代码。我想知道是否有办法在这些模板类包装器之间共享代码。请注意,我遵循教程中描述的 cython 包装方法,其中包装类保存它正在包装的 c 对象的*thisptr

// c++ header
template<_T,__Scale>
class Point
{
    Point(_T _x, _T _y) : x(_x), y(_y) {};
    // copy constructor
    template<typename _NT> Point(const Point<_NT, __Scale> &pt) : x( (_T)pt.x ), y( (_T)pt.y ) {};
    _t x, y;
    bool operator == (const Point<_T,__Scale> &pos) const
    bool operator != (const Point<_T,__Scale> &pos) const
    // and many more operators
}
typedef Point<int,1> PointA
typedef Point<int,8> PointB
... //additional typedefs
# cython interface with c++ (not shown: cdef extern from ...)
cdef cppclass Point[_T,__Scale]:
    Point(_T _x, _T _y)
    Point[_NT] Point(const Point[_NT,0] &pt)
    _T x
    _T y
    bint operator == (const Point[_T,__Scale] &pos) const
    bint operator != (const Point[_T,__Scale] &pos) const
# cython wrapper to interface with python (this is where it gets messy)
cdef class pyPointA:
    cdef PointA* thisptr
    def __cinit__(self, int x, int y):
        self.thisptr = new PointA(x,y)
    # everything in this class below this line is copied
    def x(self, setX = None):
        if(setX is None):
            return self.thisptr.x
        else:
            self.thisptr.x = setX
    def y(self, setY = None):
        if(setY is None):
            return self.thisptr.y
        else:
            self.thisptr.y = setY
    # and many more operators
cdef class pyPointB
    cdef PointB* thisptr
    def __cinit__(self, int x, int y):
        self.thisptr = new PointB(x,y)
    # everything in this class below this line is copied
    def x(self, setX = None):
        if(setX is None):
            return self.thisptr.x
        else:
            self.thisptr.x = setX
    def y(self, setY = None):
        if(setY is None):
            return self.thisptr.y
        else:
            self.thisptr.y = setY
    # and many more operators
...
#continue for additional point types

这是我尝试过的:

  • 抽象的cython基类,从中继承其他基类。好的,但是每种模板类型都需要不同的指针。
  • *thisptr存储为*void。如何应对选角?
  • 通过使用 getter getPtr() 检索来封装thisptr,但仍然强制为单个返回类型。不能声明为 Python 函数,因为 c 指针不能包装在 python 对象中。
  • 编写几个返回正确指针类型的方法,然后getPtr()方法为每个要调用的方法返回正确的 getter,以便从中获取指针。不幸的是,只有 c 函数可以返回指针,并且它们不能返回:编译器抱怨"未找到"。
  • 与上面相同,除了 getPtr() 方法返回 getter 函数的字符串名称,然后我们可以使用它getattr()来调用。但是cdef方法无法使用getattr()在Python中找到。

这样做的困难在于模板需要在 C 编译时实例化......当然,在C++中,各种模板实例化没有通用的"超类"。

通常,在这种情况下,我建议使用模板引擎(例如 jinja2(来生成您感兴趣的类的所有排列。