У меня есть следующая простая иерархия двух исключений С++:
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_;
};
Затем я экспортирую их в Python с помощью Boost-Python следующим образом. Обратите внимание на мое использование 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);
Тогда метод перевода исключений:
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());
}
Я создал следующую тестовую функцию С++, которая генерирует исключения:
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");
}
}
Наконец, тестовый код Python, который вызывает мой С++ boomTest
:
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()
До сих пор все работает. Однако, если я удаляю обработчик catch ClusterExecutionException
из Python, то это исключение будет поймано и отступит к неизвестному исключению вместо того, чтобы быть пойманным как его базовый ClusterException
.
Я пробовал в Boost-Python, регистрируя трансляцию исключений ClusterExecutionException
, чтобы зарегистрировать ее как ее базу ClusterException
, а затем ее поймают "полиморфно", но тогда она не будет поймана как ClusterExecutionException
. Как это сделать так, чтобы ClusterExecutionException
попадалось как ClusterException
и ClusterExecutionException
? Я пробовал, конечно, регистрировать это исключение ClusterExecutionException
как как ClusterException
, так и ClusterExecutionException
, но он следует за последней стратегией выигрышей, и только один работает не для обоих.
Есть ли другой способ решить эту проблему?
ОБНОВЛЕНИЕ 1: Целью этой проблемы является выяснение на стороне С++ типа инструкции except
Python, например. except ClusterException as ex:
, который неизвестен один раз внутри С++. Пересылка исключений с помощью Boost.Python вызовет функцию перевода, которая соответствует динамическому типу исключения, и статический тип улова Python неизвестен.
ОБНОВЛЕНИЕ 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])
и вывод:
************************ 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" }"
Значение, что отношение наследования "видно" от Python. Но полиморфная обработка все еще не работает.
ОБНОВЛЕНИЕ 3. Это результат работы VS dumpbin.exe
:
Используемая мной команда:
dumpbin.exe /EXPORTS /SYMBOLS C:\ClusterDK\x64\Debug\ClusterDK.dll > c:\temp\dumpbin.out
и соответствующие части вывода:
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 [email protected]@@[email protected]@@Z = @ILT+13440([email protected]@@[email protected]@@Z)
9 8 00001659 [email protected]@@[email protected][email protected][email protected]@[email protected]@[email protected]@[email protected]@[email protected]@@Z = @ILT+1620([email protected]@@[email protected][email protected][email protected]@[email protected]@[email protected]@[email protected]@[email protected]@@Z)
10 9 00001F1E [email protected]@@[email protected] = @ILT+3865([email protected]@@[email protected])
11 A 00004D4F [email protected]@@[email protected]@@Z = @ILT+15690([email protected]@@[email protected]@@Z)
12 B 000010AA [email protected]@@[email protected][email protected][email protected]@[email protected]@[email protected]@[email protected]@[email protected]@@Z = @ILT+165([email protected]@@[email protected][email protected][email protected]@[email protected]@[email protected]@[email protected]@[email protected]@@Z)
27 1A 000035D0 [email protected]@@[email protected] = @ILT+9675([email protected]@@[email protected])
28 1B 00003C7E [email protected]@@[email protected] = @ILT+11385([email protected]@@[email protected])
37 24 00002BD5 [email protected]@@[email protected]@@Z = @ILT+7120([email protected]@@[email protected]@@Z)
38 25 000034D1 [email protected]@@[email protected]@@Z = @ILT+9420([email protected]@@[email protected]@@Z)
46 2D 000D2220 [email protected]@@[email protected] = [email protected]@@[email protected] (const cluster::ClusterException::`vftable')
47 2E 000D2248 [email protected]@@[email protected] = [email protected]@@[email protected] (const cluster::ClusterExecutionException::`vftable')
52 33 00004BB5 [email protected]@[email protected]@[email protected] = @ILT+15280([email protected]@clu[email protected]@[email protected])
53 34 00004D31 [email protected]@[email protected]@[email protected] = @ILT+15660([email protected]@[email protected]@[email protected])
61 3C 00001D43 [email protected]@[email protected]@[email protected][email protected]@[email protected]@[email protected]@[email protected]@[email protected]@XZ = @ILT+3390([email protected]@[email protected]@[email protected][email protected]@[email protected]@[email protected]@[email protected]@[email protected]@XZ)
69 44 0000480E [email protected]@[email protected]@[email protected][email protected]@[email protected]@[email protected]@[email protected]@[email protected]@@Z = @ILT+14345([email protected]@[email protected]@[email protected][email protected]@[email protected]@[email protected]@[email protected]@[email protected]@@Z)
78 4D 000032FB [email protected]@[email protected]@UEBAPEBDXZ = @ILT+8950([email protected]@[email protected]@UEBAPEBDXZ)
Summary
4000 .data
5000 .idata
12000 .pdata
54000 .rdata
2000 .reloc
1000 .rsrc
C9000 .text
1000 .tls