Cython/Python/C++继承:将派生类作为参数传递给期望基类的函数

2024-09-27 19:22:02 发布

您现在位置:Python中文网/ 问答频道 /正文

我使用Cython来包装一组C++类,允许Python接口。示例代码如下:

基类.h:

#ifndef __BaseClass__
#define __BaseClass__
#include <stdio.h>
#include <stdlib.h>
#include <string>
using namespace std;
class BaseClass
{
    public:
        BaseClass(){};
        virtual ~BaseClass(){};
        virtual void SetName(string name){printf("in base set name\n");}
        virtual float Evaluate(float time){printf("in base Evaluate\n");return 0;}
        virtual bool DataExists(){printf("in base data exists\n");return false;}
};
#endif /* defined(__BaseClass__) */

DerivedClass.h:

^{pr2}$

下一节课:

#ifndef __NextDerivedClass__
#define __NextDerivedClass__

#include "DerivedClass.h"

class NextDerivedClass:public DerivedClass
{
    public:
        NextDerivedClass(){};
        virtual ~NextDerivedClass(){};
        virtual void SetObject(BaseClass *input){printf("in set object of next derived class\n");}
};
#endif /* defined(__NextDerivedClass__) */

在继承测试.pyx公司名称:

cdef extern from "BaseClass.h":
cdef cppclass BaseClass:
    BaseClass() except +
    void SetName(string)
    float Evaluate(float)
    bool DataExists()

cdef extern from "DerivedClass.h":
    cdef cppclass DerivedClass(BaseClass):
        DerivedClass() except +
        void MyFunction()
        float Evaluate(float)
        bool DataExists()
        void SetObject(BaseClass *)

cdef extern from "NextDerivedClass.h":
    cdef cppclass NextDerivedClass(DerivedClass):
        NextDerivedClass() except +
        # ***  The issue is right here ***
        void SetObject(BaseClass *)

cdef class PyBaseClass:
    cdef BaseClass *thisptr
    def __cinit__(self):
        if type(self) is PyBaseClass:
            self.thisptr = new BaseClass()
    def __dealloc__(self):
        if type(self) is PyBaseClass:
            del self.thisptr

cdef class PyDerivedClass(PyBaseClass):
    cdef DerivedClass *derivedptr
    def __cinit__(self):
        self.derivedptr = self.thisptr = new DerivedClass()
    def __dealloc__(self):
        del self.derivedptr
    # def Evaluate(self, time):
    #     return self.derivedptr.Evaluate(time)
    def SetObject(self, PyBaseClass inputObject):
         self.derivedptr.SetObject(<BaseClass *>inputObject.thisptr)

cdef class PyNextDerivedClass(PyDerivedClass):
    cdef NextDerivedClass *nextDerivedptr
    def __cinit__(self):
        self.nextDerivedptr = self.thisptr = new NextDerivedClass()
    def __dealloc__(self):
        del self.nextDerivedptr
    def SetObject(self, PyBaseClass input):
        self.nextDerivedptr.SetObject(<BaseClass *>input.thisptr)

我希望能够在Python中调用SetObject,如下所示:

在主.py公司名称:

from inheritTest import PyBaseClass as base
from inheritTest import PyDerivedClass as der
from inheritTest import PyNextDerivedClass as nextDer

#This works now!
a = der()
b = der()
a.SetObject(b)

#This doesn't work -- keeping the function declaration causes a overloaded error, not keeping it means the call below works, but it calls the inherited implementation (From derived class)
c = nextDer()
c.SetObject(b)

我原以为这是可行的,因为这些类是相互继承的,但它给了我以下错误:

参数的类型不正确:应为PyBaseClass,而get为PyDerivedClass

在函数定义中不指定类型会使它认为inputObject是纯Python对象(它没有基于C的属性,但它没有),在这种情况下,错误是:

*无法将Python对象转换为基类*

一种解决这一问题的方法就是使用不同名称的Python函数来期望不同类型的参数(例如:SetObjectWithBase、SetObjectWithDerived),然后在它们的实现中,只需调用相同的基于C的函数,并将类型转换为输入。我知道这是可行的,但我想尽量避免这样做。即使有一种方法可以捕捉函数中的类型错误,并在函数内部处理它,我认为这可能有效,但我不确定如何实现它。在

希望这个问题有意义,如果你需要更多的信息请告诉我。在

****编辑*****:已对代码进行了编辑,以便基本继承可以工作。在对它进行了更多的研究之后,我意识到问题发生在多个继承级别上,例如,请参阅上面编辑的代码。基本上,为NextDerivedClass保留SetObject声明会导致一个“不明确的重载方法”错误,不保留它允许我调用对象上的函数,但它调用继承的实现(从deriveClass)。**在


Tags: fromselfdefvirtualfloatclassevaluatecdef
3条回答

经过以下答案的大量帮助和实验,我想我理解了在Cython中实现基本继承是如何工作的,我正在回答我自己的问题,以验证/改进我的理解,并希望能帮助任何将来可能遇到相关问题的人。如果这个解释有任何错误,请在下面的评论中纠正我,我会编辑它。我不认为这是唯一的方法,所以我确信其他方法也能起作用,但这是对我有效的方法。在

概述/学到的东西:

基本上,根据我的理解,Cython足够聪明(给出适当的信息)遍历继承层次结构/树,并根据调用它的对象类型调用虚拟函数的适当实现。在

<>重要的是尝试镜像你在.pYX文件中试图包装的C++继承结构。这意味着确保:

1)导入的C++/Cython cppclasses(声明为^ {CD1>})与实际C++类做

的方式继承

2)对于每个导入的类,只声明唯一的方法/成员变量(对于在两个类中实现不同的虚拟函数,BaseClass和{}不应同时具有函数声明)。只要一个从另一个继承,函数声明只需要在基类imported中。在

3)Python包装类(即{{CD4>}/^ {CD5>})也应该以与实际C++类做

相同的方式彼此继承。

4)与上述类似,虚函数的接口只需存在于PyBase包装类中(不应同时放入这两个类中,实际运行代码时将调用正确的实现)。在

5)对于每个Python包装类的子类或继承类,您需要在__cinit__()__dealloc__()函数中同时进行if type(self) is class-name:检查。这将防止seg错误等。您不需要检查层次树中的“叶节点”(不会继承或子类化的类)

6)确保在__dealloc__()函数中,只删除当前指针(而不是任何继承的指针)

7)同样,在__cinit__()中,对于继承的类,请确保您设置了当前指针,以及指向您试图创建的类型的对象的所有派生指针(即*self.nextDerivedptr = self.derivedptr = self.thisptr = new NextDerivedClass()*

希望上面的几点在您看到下面的代码时有很大的意义,它编译并运行/按照我的需要/希望它工作。在

基类.h:

#ifndef __BaseClass__
#define __BaseClass__

#include <stdio.h>
#include <stdlib.h>
#include <string>

using namespace std;

class BaseClass
{
    public:
        BaseClass(){};
        virtual ~BaseClass(){};
        virtual void SetName(string name){printf("BASE: in set name\n");}
        virtual float Evaluate(float time){printf("BASE: in Evaluate\n");return 0;}
        virtual bool DataExists(){printf("BASE: in data exists\n");return false;}
};
#endif /* defined(__BaseClass__) */ 

DerivedClass.h:

^{pr2}$

NextDerivedClass.h:

    #ifndef __NextDerivedClass__
    #define __NextDerivedClass__

    #include "DerivedClass.h"

    class NextDerivedClass:public DerivedClass
    {
        public:
            NextDerivedClass(){};
            virtual ~NextDerivedClass(){};
            virtual void SetObject(BaseClass *input){printf("NEXT DERIVED CLASS: in set object\n");}
            virtual bool DataExists(){printf("NEXT DERIVED CLASS: in data exists \n");return true;}
    };
    #endif /* defined(__NextDerivedClass__) */

在继承测试.pyx公司名称:

#Necessary Compilation Options
#distutils: language = c++
#distutils: extra_compile_args = ["-std=c++11", "-g"]

#Import necessary modules
from libcpp cimport bool
from libcpp.string cimport string
from libcpp.map cimport map
from libcpp.pair cimport pair
from libcpp.vector cimport vector

cdef extern from "BaseClass.h":
    cdef cppclass BaseClass:
        BaseClass() except +
        void SetName(string)
        float Evaluate(float)
        bool DataExists()

cdef extern from "DerivedClass.h":
    cdef cppclass DerivedClass(BaseClass):
        DerivedClass() except +
        void MyFunction()
        void SetObject(BaseClass *)

cdef extern from "NextDerivedClass.h":
    cdef cppclass NextDerivedClass(DerivedClass):
        NextDerivedClass() except +

cdef class PyBaseClass:
    cdef BaseClass *thisptr
    def __cinit__(self):
        if type(self) is PyBaseClass:
            self.thisptr = new BaseClass()
    def __dealloc__(self):
        if type(self) is PyBaseClass:
            del self.thisptr
    def SetName(self, name):
        self.thisptr.SetName(name)
    def Evaluate(self, time):
        return self.thisptr.Evaluate(time)
    def DataExists(self):
        return self.thisptr.DataExists()

cdef class PyDerivedClass(PyBaseClass):
    cdef DerivedClass *derivedptr
    def __cinit__(self):
        if type(self) is PyDerivedClass:
            self.derivedptr = self.thisptr = new DerivedClass()
    def __dealloc__(self):
        if type(self) is PyBaseClass:
            del self.derivedptr
    def SetObject(self, PyBaseClass inputObject):
        self.derivedptr.SetObject(<BaseClass *>inputObject.thisptr)
    def MyFunction(self):
        self.derivedptr.MyFunction()

cdef class PyNextDerivedClass(PyDerivedClass):
    cdef NextDerivedClass *nextDerivedptr
    def __cinit__(self):
        self.nextDerivedptr = self.derivedptr = self.thisptr = new NextDerivedClass()
    def __dealloc__(self):
        del self.nextDerivedptr

在测试.py公司名称:

from inheritTest import PyBaseClass as base
from inheritTest import PyDerivedClass as der
from inheritTest import PyNextDerivedClass as nextDer

a = der()
b = der()
a.SetObject(b)
c = nextDer()
a.SetObject(c)
c.DataExists()
c.SetObject(b)
c.Evaluate(0.3)


baseSig = base()
signal = der()
baseSig.SetName('test')
signal.SetName('testingone')
baseSig.Evaluate(0.3)
signal.Evaluate(0.5)
signal.SetObject(b)
baseSig.DataExists()
signal.DataExists()

请注意,当我打电话给:

c = nextDer()
c.Evaluate(0.3)

它的工作方式是Cython沿着继承树寻找 “最新”实施评估。如果它存在于NextDerivedClass.h中,它会调用它(我已经尝试过了,它起作用了),但是由于它不在那里,它会更进一步并检查DerivedClass。该功能在那里实现,因此输出为:

>> DERIVED CLASS: in Evaluate

我希望这对将来的人有所帮助,同样,如果我的理解有错误,或者只是语法/语法方面的错误,请随时在下面发表评论,我会尽力解决它们。再次感谢下面的回答者,这是他们答案的总结,只是为了帮助验证我的理解。谢谢!在

编写的代码无法编译。我怀疑你的实数PyDerivedClass并不是真的从PyBaseClass派生出来的,就像它真的那样

(<DerivedClass*>self.thisptr).SetObject(inputObject.thisptr)

这也可以解释你得到的类型错误,这是一个我无法重现的错误。在

老实说,这看起来像个虫子。传入的对象是所需类的一个实例,但它仍然抛出一个错误。你可以把它放在cython用户的邮件列表中,这样主要的开发人员就可以看到它了。在

一种可能的解决方法是定义一个表示两种类型参数的fused type,并在方法中使用它。不过,这似乎有点过分了。在

相关问题 更多 >

    热门问题