[英]Polymorphic exception handling: How to catch subclass exception?
I have the following simple hierarchy of two C++ exceptions: 我有以下两个C ++异常的简单层次结构:
class LIB_EXP ClusterException : public std::exception {
public:
ClusterException() { }
ClusterException(const std::string& what) { init(what); }
virtual const char* what() const throw() { return what_.c_str(); }
virtual ~ClusterException() throw() {}
virtual ClusterException* clone() { return new ClusterException(*this); }
protected:
void init(const std::string& what) { what_ = what; }
private:
std::string what_;
};
class LIB_EXP ClusterExecutionException : public ClusterException {
public:
ClusterExecutionException(const std::string& jsonResponse);
std::string getErrorType() const throw() { return errorType_; }
std::string getClusterResponse() const throw() { return clusterResponse_; }
virtual ~ClusterExecutionException() throw() {}
virtual ClusterExecutionException* clone() { return new ClusterExecutionException(*this); }
private:
std::string errorType_;
std::string clusterResponse_;
};
I then export them to Python with Boost-Python as follows. 然后我使用Boost-Python将它们导出到Python,如下所示。 Note my use of the
bases
to make sure that the inheritance relationship is preserved in the translation: 请注意我使用
bases
来确保在转换中保留继承关系:
class_<ClusterException> clusterException("ClusterException", no_init);
clusterException.add_property("message", &ClusterException::what);
clusterExceptionType = clusterException.ptr();
register_exception_translator<ClusterException>(&translateClusterException);
class_<ClusterExecutionException, bases<ClusterException> > clusterExecutionException("ClusterExecutionException", no_init);
clusterExecutionException.add_property("message", &ClusterExecutionException::what)
.add_property("errorType", &ClusterExecutionException::getErrorType)
.add_property("clusterResponse", &ClusterExecutionException::getClusterResponse);
clusterExecutionExceptionType = clusterExecutionException.ptr();
register_exception_translator<ClusterExecutionException>(&translateClusterExecutionException);
Then the exception translation method: 然后是异常翻译方法:
static PyObject *clusterExceptionType = NULL;
static void translateClusterException(ClusterException const &exception) {
assert(clusterExceptionType != NULL);
boost::python::object pythonExceptionInstance(exception);
PyErr_SetObject(clusterExceptionType, pythonExceptionInstance.ptr());
}
static PyObject *clusterExecutionExceptionType = NULL;
static void translateClusterExecutionException(ClusterExecutionException const &exception) {
assert(clusterExecutionExceptionType != NULL);
boost::python::object pythonExceptionInstance(exception);
PyErr_SetObject(clusterExecutionExceptionType, pythonExceptionInstance.ptr());
}
I created the following test C++ function that throws the exceptions: 我创建了以下测试C ++函数,抛出异常:
static void boomTest(int exCase) {
switch (exCase) {
case 0: throw ClusterException("Connection to server failed");
break;
case 1: throw ClusterExecutionException("Error X while executing in the cluster");
break;
default: throw std::runtime_error("Unknown exception type");
}
}
Finally the Python test code that calls my C++ boomTest
: 最后调用我的C ++
boomTest
的Python测试代码:
import cluster
reload(cluster)
from cluster import ClusterException, ClusterExecutionException
def test_exception(exCase):
try:
cluster.boomTest(exCase)
except ClusterException as ex:
print 'Success! ClusterException gracefully handled:' \
'\n message="%s"' % ex.message
except ClusterExecutionException as ex:
print 'Success! ClusterExecutionException gracefully handled:' \
'\n message="%s"' \
'\n errorType="%s"' \
'\n clusterResponse="%s"' % (ex.message, ex.errorType, ex.clusterResponse)
except:
print 'Caught unknown exception: %s "%s"' % (sys.exc_info()[0], sys.exc_info()[1])
def main():
print '\n************************ throwing ClusterException ***********************************************************************'
test_exception(0)
print '\n************************ throwing ClusterExecutionException **************************************************************'
test_exception(1)
print '\n************************ throwing std::runtime_error *********************************************************************'
test_exception(2)
if __name__ == "__main__":
main()
Up to here it all works. 到这里一切都有效。 However if I remove the
ClusterExecutionException
catch handler from Python then this exception will be caught and fallback to an unknown exception instead of being caught as its base ClusterException
. 但是,如果我从Python中删除
ClusterExecutionException
catch处理程序, ClusterExecutionException
捕获此异常并回ClusterException
未知异常,而不是作为其基本ClusterException
。
I have tried in Boost-Python while registering the exception translation of ClusterExecutionException
to register it as its base ClusterException
and then it gets caught "polymorphically" but then it won't be caught as a ClusterExecutionException
. 我在Boost-Python中尝试了注册
ClusterExecutionException
的异常转换, ClusterExecutionException
其注册为基本的ClusterException
,然后它被“多态”捕获,但它不会被捕获为ClusterExecutionException
。 How can make it so that ClusterExecutionException
gets caught as both ClusterException
and ClusterExecutionException
? 如何使
ClusterExecutionException
被捕获为ClusterException
和ClusterExecutionException
? I have tried of course registering this ClusterExecutionException
exception as both ClusterException
and ClusterExecutionException
but it follows a last wins strategy, and only one works not both. 我当然尝试将此
ClusterExecutionException
异常作为ClusterException
和ClusterExecutionException
但它遵循最后一个获胜策略,并且只有一个不同时工作。
Is there any other way to solve this? 有没有其他方法可以解决这个问题?
UPDATE 1: The wholy grail of this problem is to find out on the C++ side the type of the except
Python statement eg except ClusterException as ex:
which is unknown once inside the C++ side. 更新1:这个问题的关键是要在C ++端找到
except
Python语句except ClusterException as ex:
的类型,例如except ClusterException as ex:
在C ++方面是未知的。 The exception translate by Boost.Python will call the translate function that corresponds to the dynamic type of the exception and the Python catch static type is not known. Boost.Python转换的异常将调用与异常的动态类型相对应的translate函数,并且Python catch静态类型未知。
UPDATE 2: As suggested changing the Python code to the following ie adding print(type(ex).__bases__)
gives: 更新2:建议将Python代码更改为以下内容,即添加
print(type(ex).__bases__)
给出:
def test_exception(exCase):
try:
cluster.boomTest(exCase)
except ClusterException as ex:
print(type(ex).__bases__)
print 'Success! ClusterException gracefully handled:' \
'\n message="%s"' % ex.message
except ClusterExecutionException as ex:
print(type(ex).__bases__)
print 'Success! ClusterExecutionException gracefully handled:' \
'\n message="%s"' \
'\n errorType="%s"' \
'\n clusterResponse="%s"' % (ex.message, ex.errorType, ex.clusterResponse)
except:
print 'Caught unknown exception: %s "%s"' % (sys.exc_info()[0], sys.exc_info()[1])
and the output: 和输出:
************************ throwing ClusterException ***********************************************************************
(<type 'Boost.Python.instance'>,)
Success! ClusterException gracefully handled:
message="Connection to server failed"
************************ throwing ClusterExecutionException **************************************************************
(<class 'cluster.ClusterException'>,)
Success! ClusterExecutionException gracefully handled:
message="Error X while executing in the cluster"
errorType="LifeCycleException"
clusterResponse="{ "resultStatus": "Error", "errorType": "LifeCycleException", "errorMessage": "Error X while executing in the cluster" }"
Meaning that the inheritance relationship is "seen" from Python. 意味着继承关系是从Python“看到”的。 But the polymorphic handling still doesn't work.
但多态处理仍然无效。
UPDATE 3 This is the output of running VS dumpbin.exe
: 更新3这是运行VS
dumpbin.exe
的输出:
The command I used is: 我使用的命令是:
dumpbin.exe /EXPORTS /SYMBOLS C:\ClusterDK\x64\Debug\ClusterDK.dll > c:\temp\dumpbin.out
and the relevant parts of the output: 以及输出的相关部分:
Microsoft (R) COFF/PE Dumper Version 11.00.50727.1
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file C:\ClusterDK\x64\Debug\ClusterDK.dll
File Type: DLL
Section contains the following exports for ClusterDK.dll
00000000 characteristics
5A1689DA time date stamp Thu Nov 23 09:42:02 2017
0.00 version
1 ordinal base
78 number of functions
78 number of names
ordinal hint RVA name
8 7 00004485 ??0ClusterException@cluster@@QEAA@AEBV01@@Z = @ILT+13440(??0ClusterException@cluster@@QEAA@AEBV01@@Z)
9 8 00001659 ??0ClusterException@cluster@@QEAA@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z = @ILT+1620(??0ClusterException@cluster@@QEAA@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z)
10 9 00001F1E ??0ClusterException@cluster@@QEAA@XZ = @ILT+3865(??0ClusterException@cluster@@QEAA@XZ)
11 A 00004D4F ??0ClusterExecutionException@cluster@@QEAA@AEBV01@@Z = @ILT+15690(??0ClusterExecutionException@cluster@@QEAA@AEBV01@@Z)
12 B 000010AA ??0ClusterExecutionException@cluster@@QEAA@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z = @ILT+165(??0ClusterExecutionException@cluster@@QEAA@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z)
27 1A 000035D0 ??1ClusterException@cluster@@UEAA@XZ = @ILT+9675(??1ClusterException@cluster@@UEAA@XZ)
28 1B 00003C7E ??1ClusterExecutionException@cluster@@UEAA@XZ = @ILT+11385(??1ClusterExecutionException@cluster@@UEAA@XZ)
37 24 00002BD5 ??4ClusterException@cluster@@QEAAAEAV01@AEBV01@@Z = @ILT+7120(??4ClusterException@cluster@@QEAAAEAV01@AEBV01@@Z)
38 25 000034D1 ??4ClusterExecutionException@cluster@@QEAAAEAV01@AEBV01@@Z = @ILT+9420(??4ClusterExecutionException@cluster@@QEAAAEAV01@AEBV01@@Z)
46 2D 000D2220 ??_7ClusterException@cluster@@6B@ = ??_7ClusterException@cluster@@6B@ (const cluster::ClusterException::`vftable')
47 2E 000D2248 ??_7ClusterExecutionException@cluster@@6B@ = ??_7ClusterExecutionException@cluster@@6B@ (const cluster::ClusterExecutionException::`vftable')
52 33 00004BB5 ?clone@ClusterException@cluster@@UEAAPEAV12@XZ = @ILT+15280(?clone@ClusterException@cluster@@UEAAPEAV12@XZ)
53 34 00004D31 ?clone@ClusterExecutionException@cluster@@UEAAPEAV12@XZ = @ILT+15660(?clone@ClusterExecutionException@cluster@@UEAAPEAV12@XZ)
61 3C 00001D43 ?getErrorType@ClusterExecutionException@cluster@@QEBA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ = @ILT+3390(?getErrorType@ClusterExecutionException@cluster@@QEBA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ)
69 44 0000480E ?init@ClusterException@cluster@@IEAAXAEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z = @ILT+14345(?init@ClusterException@cluster@@IEAAXAEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z)
78 4D 000032FB ?what@ClusterException@cluster@@UEBAPEBDXZ = @ILT+8950(?what@ClusterException@cluster@@UEBAPEBDXZ)
Summary
4000 .data
5000 .idata
12000 .pdata
54000 .rdata
2000 .reloc
1000 .rsrc
C9000 .text
1000 .tls
My python skills are rusty and I haven't tested this, so this may need further improvements, but try adding the exception translation method to handle the base class exception type: 我的python技能生锈了,我还没有测试过,所以这可能需要进一步改进,但是尝试添加异常转换方法来处理基类异常类型:
static PyObject *clusterExecutionAsClusterExceptionType = NULL;
static void translateClusterExecutionAsClusterException(ClusterException const &exception) {
ClusterExecutionException* upcasted = dynamic_cast<ClusterExecutionException*>(&exception);
if (upcasted)
{
assert(clusterExecutionAsClusterExceptionType != NULL);
boost::python::object pythonExceptionInstance(*upcasted);
PyErr_SetObject(clusterExecutionAsClusterExceptionType, pythonExceptionInstance.ptr());
}
}
register_exception_translator<ClusterException>(&translateClusterExecutionAsClusterException);
I don't know about your C++ code. 我不知道你的C ++代码。 But there is small problem with your python code.
但是你的python代码有一个小问题。 Catch
ClusterExecutionException
before ClusterException
. 在
ClusterExecutionException
之前捕获ClusterException
。 You should always put child exception handler before base exception. 您应该始终在基本异常之前放置子异常处理程序。
In test_exception
if ClusterExecutionException
raised it will be catch by ClusterException
before it could reach ClusterExecutionException
. 在
test_exception
如果ClusterExecutionException
引发,它将被ClusterException
捕获,然后才能到达ClusterExecutionException
。
code should be as below code 代码应该如下代码
def test_exception(exCase):
try:
cluster.boomTest(exCase)
except ClusterExecutionException as ex:
print 'Success! ClusterExecutionException gracefully handled:' \
'\n message="%s"' \
'\n errorType="%s"' \
'\n clusterResponse="%s"' % (ex.message, ex.errorType, ex.clusterResponse)
except ClusterException as ex:
print 'Success! ClusterException gracefully handled:' \
'\n message="%s"' % ex.message
except:
print 'Caught unknown exception: %s "%s"' % (sys.exc_info()[0], sys.exc_info()[1])
now do I have tried in Boost-Python while registering the exception translation of ClusterExecutionException to register it as its base ClusterException
, what you have mentioned in question. 现在
I have tried in Boost-Python while registering the exception translation of ClusterExecutionException to register it as its base ClusterException
,这就是你所提到的问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.