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

How to create a child class in python when using cppyy?

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

这是我的问题。

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

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

// 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 以添加更多功能

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 not an acceptable base: no virtual destructor"。我知道 CHSTradeSpi 是抽象的 class,但是如何创建它的子 class?

先谢谢了。

***************更新*********************
非常感谢 Wim Lavrijsen。 我改变了我的计划。首先我用C++写了一个派生的class 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 {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",所以我猜这样,class CTradeSpi(spi) 没有覆盖spi,什么都没有运行。我不知道为什么。 谢谢。

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

如果你绝对想继续,你可以插入一个带有虚拟析构函数的虚拟 class。如果通过原始基在 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

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

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

您无法替换 metaclass:是 metaclass 插入了蹦床,因此通过使用 abc.ABCMeta,它会被禁用。是的,没有错误,但也没有派送。

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