cython c++ 类继承不能实例化派生类

Posted

技术标签:

【中文标题】cython c++ 类继承不能实例化派生类【英文标题】:cython c++ class inheritance cannot instantiate a derived class 【发布时间】:2017-09-21 11:38:00 【问题描述】:

我正在尝试用 Cython 包装一些 c++ 类。对于基类,一切正常。对于具有相同参数(在其构造函数中)与其各自基类的参数的派生类,也会发生同样的情况。但是,我无法实例化具有与其基类不同数量的参数(在其构造函数中)的派生类。

例如:

h 文件
// CYH.h file
#ifndef __CYH_H__
#define __CYH_H__

#include <iostream>

class Base

public:
  Base(double);
  ~Base();
  double A;
  double getA();
  void setA(double);
;

class Derived : public Base

public:
   Derived(double, double);
   ~Derived();

   double B;
;
#endif
cpp 文件
// cyh.cpp file
#include <iostream>
#include <string>
#include "include/cyh.h"

Base::Base(double a)

    A = a;


Base::~Base()



double Base::getA()
    return A;


void Base::setA(double a_)
    A = a_;


Derived::Derived(double a, double b) : Base(a)

    B = b;


Derived::~Derived()


和 pyx 文件
cdef extern from "include/cyh.h":
    cdef cppclass Base:
        Base(double)
        double getA()
        void setA(double)

    cdef cppclass Derived(Base):
         Derived(double, double)

cdef class cyBase:
    cdef Base *thisptr
    def __cinit__(self, double a):
        if type(self) is cyBase:
            self.thisptr = new Base(a)

    def __dealloc__(self):
        if type(self) is cyBase:
            del self.thisptr

    @property
    def A(self):
        return self.thisptr.getA()

    @A.setter
    def A(self, value):
        self.thisptr.setA(value)


cdef class cyDerived(cyBase):
    cdef Derived *thisptrDerived
    def __cinit__(self, double a, double b):
        if type(self) is cyDerived:
            self.thisptrDerived = self.thisptr = new Derived(a, b)

    def __dealloc__(self):
        if type(self) is cyDerived:
            del self.thisptrDerived

它编译并且 pyd 构建成功。但是,当我尝试使用以下方法实例化 cyDerived 类时:

mycyObj = cyDerived(a=2., b=3.)

我收到以下错误:

__cinit__() got an unexpected keyword argument 'b'

蚂蚁知道我做错了什么?

非常感谢!

【问题讨论】:

这可能解决了你的问题:***.com/a/33091422/5769463 非常感谢您的评论和链接。我在groups.google.com/forum/#!topic/cython-users/N-jXUWLzd6o 指出的基类中使用了 *args 和 **kwards。但是,据说这可能会带来效率损失(我不明白为什么)。我想知道是否有更有效的方法。 一种选择是在基类构造函数中“预期”b 并在那里忽略它 - 但这会使代码不那么干净,所以我只会这样做***.com/a/13811280/5769463 我使用了 *args 和 **kwards。为什么这会影响性能?在哪里?在类实例化期间? 【参考方案1】:

其实这是一个很有趣的问题:使用*args**kwargs的性能损失是多少,哪些场景需要考虑?

从理论上看,如果使用*args**kwargs的方案,cyBase类的构造函数(每次调用)还有以下额外的工作需要做:

    获取指向argskwargs 的指针并在垃圾收集器中注册这些引用, 查字典(一些开销), 当不再需要时,取消注册垃圾收集器中的引用,以便可以销毁此字典。

我的假设是,注册/注销可能是个问题,因为参考更新需要获取 GIL。最好不要经常这样做。

没有*args 的解决方案似乎无需更改引用计数器即可工作。

到目前为止我的理论,但我希望看到一些实证结果,这可能会令人惊讶,所以如果你有一些数据,请与我们分享。


重现我的结果的代码:

#no_kwargs.pyx:
cdef class cyBase:
    def __cinit__(self, double a):
        pass

cdef class cyDerived:
    def __cinit__(self, double a):
        self.a=a


#kwargs.pyx
cdef class cyBase:
    def __cinit__(self,  *argv, **kwargs):
        pass

cdef class cyDerived:
    def __cinit__(self, double a):
        self.a=a

运行它

python -m cython XXX.pyx 

并比较生成的 c 文件。差异基本上在 cyBase::__cinit__() 发生整个引用计数的地方:

static int __pyx_pw_4test_6cyBase_1__cinit__(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds) 
   ...
  __Pyx_INCREF(__pyx_args);  // <----------- HERE
  __pyx_v_argv = __pyx_args;
   ...

  /* function exit code */
  __Pyx_XDECREF(__pyx_v_argv); // <----------- HERE
  ...
  return __pyx_r;

kwargs这里好像没用过。

【讨论】:

您好,非常感谢您的宝贵回答。实际上,在 cyDerived 类中,我存储了大量数据,然后将这些数据传递给数值求解器进行计算。但是,对于我的每个模拟,我最多需要大约 100 个此类实例。我用这两种方法做了一个基准测试,在一个循环中,我用这两种方法实例化了 1000 个类,并且花费了完全相同的时间。但是,正如您所建议的,我现在将使用不带 *args 的解决方案。

以上是关于cython c++ 类继承不能实例化派生类的主要内容,如果未能解决你的问题,请参考以下文章

C++基类和派生类的构造函数

c++继承总结

C++ 容器与继承

接口抽象类

C++ 派生类访问属性

c++ 三大特性之继承