使用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 中发布。
我使用 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.
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 中发布。