简体   繁体   English

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

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

I use cppyy to allow python call C++ functions and classes.我使用 cppyy 允许 python 调用 C++ 函数和类。 But I don't know how to create a child class of imported C++ function.但是我不知道如何创建一个导入的 C++ function 的子 class。

Here is my problem.这是我的问题。

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

Here is the class CHSTradeSpi in hearder file.这是听者文件中的 class CHSTradeSpi。 I simplized it and keep the first func in this class.我对其进行了简化并将第一个函数保留在此 class 中。

// C++ header file
#include "HSStruct.h"   // this header file seems not directly related to my problem
class  CHSTradeSpi
{
public:
    virtual void OnFrontConnected(){};
};

Then I tried to create a child class of CHSTradeSpi in Python to add more functions然后我尝试在 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")

it failed and says "CHSTradeSpi not an acceptable base: no virtual destructor".它失败并说“CHSTradeSpi 不是一个可接受的基础:没有虚拟析构函数”。 I know CHSTradeSpi is abstract class, but then how to create its child class?我知道 CHSTradeSpi 是抽象的 class,但是如何创建它的子 class?

Thank you ahead.提前谢谢你。

*************UPDATE********************* *************更新*********************
Many thanks to Wim Lavrijsen.非常感谢 Wim Lavrijsen。 I changed my plan.我改变了我的计划。 First I wrote a derived class CMyTradeSpi in C++ to get an instance.首先我在 C++ 中写了一个派生的 class CMyTradeSpi 来获取实例。

#include "../include/HSDataType.h"
#include "../include/HSTradeApi.h"
class CMyTradeSpi : public CHSTradeSpi
{
public:
     void OnFrontConnected();
};

Then I import to python然后我导入到 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

I got an error point to "public CMyTradeSpi {"我得到一个指向“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 {

it seems also need a constructor.似乎还需要一个构造函数。

**************** UPDATE 2 ******************* ****************** 更新 2 *******************
Since above error, I tried to create an instance in Python using python abc lib.由于上述错误,我尝试使用 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")

It shows no error.它显示没有错误。 But it did not print out "OnFrontConnected", so I guess in this way, class CTradeSpi(spi) did not override spi and nothing has run.但它没有打印出“OnFrontConnected”,所以我猜这样,class CTradeSpi(spi) 没有覆盖 spi 并且没有运行。 I don't know why.我不知道为什么。 Thank you.谢谢你。

It's not about the base class being an abstract base, but precisely about it not having a virtual destructor.这不是关于基础 class 是一个抽象基础,而是关于它没有虚拟析构函数。 Not having a virtual destructor means that if the derived instance gets deleted through a pointer with the type of the base class, the destructor of the derived class is not called.没有虚拟析构函数意味着如果派生实例通过基类 class 类型的指针删除,则派生 class 的析构函数不会被调用。 If OTOH, the destructor is virtual, both constructors are called as they should.如果 OTOH,析构函数是虚拟的,那么两个构造函数都会被调用。 Iow., w/oa virtual destructor, the python instance will leak (never be garbage collected), and therefore derivation of such base classes is disabled. Iow.,没有虚拟析构函数,python 实例将泄漏(永远不会被垃圾收集),因此此类基类的派生被禁用。

If you absolutely want to go ahead anyway, you can interject a dummy class with a virtual destructor.如果你绝对想要 go 无论如何,你可以插入一个带有虚拟析构函数的虚拟 class 。 You'd still have the same problem if the instance gets deleted in C++ through the original base.如果实例通过原始基础在 C++ 中删除,您仍然会遇到同样的问题。 However, if you can make sure that the python instance only ever gets deleted in python, you'll be okay.但是,如果您可以确保 python 实例仅在 python 中被删除,那么您会没事的。 Here is an example of such a workaround:以下是此类解决方法的示例:

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

EDIT : A simple way to get past the constructor problem, is to add, next to the using, a constructor that does exist.编辑:解决构造函数问题的一种简单方法是在使用旁边添加一个确实存在的构造函数。 That way, the default is not generated.这样,不会生成默认值。 I have, however, not been able to write a simple code that does that (you can introspect the base class to generate one, but it's not pretty).但是,我无法编写一个简单的代码来执行此操作(您可以自省基础 class 以生成一个,但这并不漂亮)。 If you only have one class, then this could be a workaround.如果您只有一个 class,那么这可能是一种解决方法。 Just add eg {0}WithVDtor(int i): {0}(i) {{}} just above the using in the code, if there is such a constructor (change arguments as needed).如果有这样的构造函数,只需在代码中的using上方添加例如{0}WithVDtor(int i): {0}(i) {{}} (根据需要更改 arguments)。

I'm working on changes to see whether I can relax the requirement of a virtual destructor.我正在研究是否可以放宽对虚拟析构函数的要求。 I still have a crash left in one case.在一个案例中,我仍然有一个崩溃。

You can not replace the metaclass: it is the metaclass that interject the trampoline, so by using abc.ABCMeta , that gets disabled.您不能替换元类:它是插入蹦床的元类,因此通过使用abc.ABCMeta ,它会被禁用。 Yes, no error, but also no dispatch.是的,没有错误,但也没有调度。

UPDATE : As part of the changes to support multiple inheritance across the language barrier, the kept object on the C++ side is now the trampoline, so a virtual destructor is no longer required in the base.更新:作为跨语言障碍支持多个 inheritance 的更改的一部分,C++ 端保留的 object 现在是蹦床,因此不再需要虚拟析构函数。 There is still a warning, as the same caveat remains: deletion on the C++ side will leak the Python object.仍然有一个警告,因为同样的警告仍然存在:C++ 端的删除将泄漏 Python object。 To be released soon in 1.7.2.即将在 1.7.2 中发布。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM