使用cppyy时如何在python中创建子类?

Posted

技术标签:

【中文标题】使用cppyy时如何在python中创建子类?【英文标题】:How to create a child class in python when using cppyy? 【发布时间】:2020-06-10 12:34:47 【问题描述】:

我使用 cppyy 来允许 python 调用 C++ 函数和类。但我不知道如何创建导入的 C++ 函数的子类。

这是我的问题。

import cppyy
cppyy.include('/include/HSTradeApi.h')  //include the CHSTradeSpi class
cppyy.load_library('win64/HSTradeApi')

这是听者文件中的 CHSTradeSpi 类。我简化了它并保留了这个类中的第一个函数。

// C++ header file
#include "HSStruct.h"   // this header file seems not directly related to my problem
class  CHSTradeSpi

public:
    virtual void OnFrontConnected();
;

然后我尝试在 Python 中创建 CHSTradeSpi 的子类以添加更多功能

class CTradeSpi(cppyy.gbl.CHSTradeSpi):

    def __init__(self, tapi):
        super().__init__(self)  // is this line wrong?
        self.tapi = tapi  

    def OnFrontConnected(self) -> "void":  
        print("OnFrontConnected")
        authfield = cppyy.gbl.CHSReqAuthenticateField()  # defined in HSSruct.h
        authfield.BrokerID = BROKERID
        authfield.UserID = USERID
        authfield.AppID = APPID
        authfield.AuthCode = AuthCode  #
        self.tapi.ReqAuthenticate(authfield, 0)
        print("send ReqAuthenticate ok")

它失败并显示“CHSTradeSpi 不是可接受的基础:没有虚拟析构函数”。我知道 CHSTradeSpi 是抽象类,但是如何创建它的子类呢?

谢谢你。

***************更新********************* 非常感谢 Wim Lavrijsen。 我改变了我的计划。首先我用 C++ 写了一个派生类 CMyTradeSpi 来获取实例。

#include "../include/HSDataType.h"
#include "../include/HSTradeApi.h"
class CMyTradeSpi : public CHSTradeSpi

public:
     void OnFrontConnected();
;

然后我导入到python

import cppyy
cppyy.include('/include/HSTradeApi.h')  //include the CHSTradeSpi class
cppyy.load_library('win64/HSTradeApi')
cppyy.include('/include/newTrade.h')  ## class CMyTradeSpi in it

virt_spi = AddVirtualDtor(cppyy.gbl.CMyTradeSpi)  # call CMyTradeSpi

class CTradeSpi(virt_spi):

    def __init__(self, tapi):  
        virt_spi.__init__(self)  
        self.tapi = tapi

我得到一个指向“public CMyTradeSpi ”的错误点

input_line_29:18:3: error: call to implicitly-deleted default constructor of '::workaround::CMyTradeSpiWithVDtor'
  Dispatcher1() 
  ^
input_line_27:2:34: note: default constructor of 'CMyTradeSpiWithVDtor' is implicitly deleted because base class 'CMyTradeSpi' has no default constructor
    class CMyTradeSpiWithVDtor : public CMyTradeSpi 

它似乎也需要一个构造函数。

**************** 更新 2 ******************* 由于上述错误,我尝试使用 python abc lib 在 Python 中创建一个实例。

import time
import cppyy
import abc
cppyy.include('/include/HSTradeApi.h')
cppyy.load_library('win64/HSTradeApi')

def AddVirtualDtor(cls):
    #dname = cls.__name__+"WithVDtor"
    cppyy.cppdef("""namespace workaround 
    class 0WithVDtor : public 1 
    public:
        using 0::0;
        virtual ~0WithVDtor() 
    ; """.format(cls.__name__, cls.__cpp_name__))
    return getattr(cppyy.gbl.workaround, "0WithVDtor".format(cls.__name__))

spi = AddVirtualDtor(cppyy.gbl.CHSTradeSpi)

class CTradeSpi(spi):
    __metaclass__ = abc.ABCMeta

    def __init__(self, tapi):  
        spi.__init__(self) 
        self.tapi = tapi  

    def OnFrontConnected(self) -> "void":   
        print("OnFrontConnected") 
        authfield = cppyy.gbl.CHSReqAuthenticateField() 
        authfield.HSAccountID = ACCOUNTID
        authfield.HSPassword = PASSWORD
        authfield.HSAppID = APPID
        authfield.HSAuthCode = AuthCode  #
        self.tapi.ReqAuthenticate(authfield, 0)
        print("send ReqAuthenticate ok")

它没有显示错误。但它没有打印出“OnFrontConnected”,所以我猜这样,CTradeSpi(spi) 类没有覆盖 spi 并且没有运行。我不知道为什么。 谢谢。

【问题讨论】:

【参考方案1】:

这不是关于基类是抽象基类,而是关于它没有虚拟析构函数。没有虚拟析构函数意味着如果派生实例通过具有基类类型的指针被删除,则不会调用派生类的析构函数。如果 OTOH,析构函数是虚拟的,则两个构造函数都会被调用。 Iow.,没有虚拟析构函数,python 实例将泄漏(永远不会被垃圾收集),因此此类基类的派生被禁用。

如果你绝对想继续,你可以插入一个带有虚拟析构函数的虚拟类。如果实例通过原始基础在 C++ 中被删除,您仍然会遇到同样的问题。但是,如果你能确保 python 实例只在 python 中被删除,你会没事的。以下是此类解决方法的示例:

import cppyy

cppyy.cppdef("""
class CHSTradeSpi 
public:
    virtual void OnFrontConnected() = 0;
;""")

def AddVirtualDtor(cls):
    dname = cls.__name__+"WithVDtor"
    cppyy.cppdef("""namespace workaround 
    class 0WithVDtor : public 1 
    public:
        using 0::0;
        virtual ~0WithVDtor() 
    ; """.format(cls.__name__, cls.__cpp_name__))
    return getattr(cppyy.gbl.workaround, "0WithVDtor".format(cls.__name__))

class CTradeSpi(AddVirtualDtor(cppyy.gbl.CHSTradeSpi)):
    def __init__(self, tapi):
        super(CTradeSpi, self).__init__()
        self.tapi = tapi

    def OnFrontConnected(self):
        # etc ...
        pass

编辑:解决构造函数问题的一种简单方法是在 using 旁边添加一个确实存在的构造函数。这样,不会生成默认值。但是,我无法编写一个简单的代码来做到这一点(您可以自省基类来生成一个,但这并不漂亮)。如果你只有一个类,那么这可能是一种解决方法。只需添加例如0WithVDtor(int i) : 0(i) 就在代码中 using 的上方,如果有这样的构造函数(根据需要更改参数)。

我正在做一些改变,看看我是否可以放宽对虚拟析构函数的要求。在一个案例中,我还有一个崩溃。

您不能替换元类:它是插入蹦床的元类,因此通过使用abc.ABCMeta,它会被禁用。是的,没有错误,但也没有调度。

更新:作为跨语言障碍支持多重继承的更改的一部分,C++ 端的保留对象现在是蹦床,因此基础中不再需要虚拟析构函数。仍然有一个警告,因为同样的警告仍然存在:在 C++ 端删除将泄漏 Python 对象。即将在 1.7.2 中发布。

【讨论】:

非常感谢。我试过了,但得到一个错误,它似乎也需要一个构造器。对不起,我需要时间来学习更多关于类和继承的 C++ ......我更新到原来的问题。 不,这是上游的一个已知错误(有一个打开的错误报告):如果没有,它会添加一个默认构造函数,因为它不知道“使用”失败。如果构造函数被显式地重新声明,这将不是问题,但这会使 dtor 插入函数不那么通用。让我想一个解决方法。 谢谢。然后我必须在 python 中创建实例。既然我有 AddVirtualDtor(cppyy.gbl.CHSTradeSpi),我可以使用 "import abc" 创建一个实例吗?请查看我更新的问题。

以上是关于使用cppyy时如何在python中创建子类?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 C++ 中的子类中创建父对象数组?

核心数据。如何使用在 NSManagedObject 子类中创建的方法

如何在 python 抽象类中创建抽象属性

如何在cppyy中加载库?

cppyy - 如何调用接受集合的 c++ 函数?

在 python 中使用 cppyy 时,指针变量引发错误为未知类型