首页 > 解决方案 > 使用cppyy时如何在python中创建子类?

问题描述

我使用 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 {

似乎还需要一个构造函数。

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

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 {0}WithVDtor : public {1} {{
    public:
        using {0}::{0};
        virtual ~{0}WithVDtor() {{}}
    }}; }}""".format(cls.__name__, cls.__cpp_name__))
    return getattr(cppyy.gbl.workaround, "{0}WithVDtor".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 并且没有运行。我不知道为什么。谢谢你。

标签: pythonclasscppyy

解决方案


这不是关于基类是抽象基础,而是关于它没有虚拟析构函数。没有虚拟析构函数意味着如果派生实例通过具有基类类型的指针被删除,则不会调用派生类的析构函数。如果 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 {0}WithVDtor : public {1} {{
    public:
        using {0}::{0};
        virtual ~{0}WithVDtor() {{}}
    }}; }}""".format(cls.__name__, cls.__cpp_name__))
    return getattr(cppyy.gbl.workaround, "{0}WithVDtor".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

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

我正在研究是否可以放宽对虚拟析构函数的要求。在一个案例中,我仍然有一个崩溃。

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

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


推荐阅读