All C++ exceptions are converted to Python exceptions and all Python exceptions are converted to C++ exceptions, to allow exception propagation through multiple levels of callbacks, while retaining the option to handle the outstanding exception as needed in either language. To preserve an exception across the language boundaries, it must derive from
std::exception
. If preserving the exception (or its type) is not possible, generic exceptions are used to propagate the exception:
Exception
in Python or
CPyCppyy::PyException
in C++.
In the most common case of an instance of a C++ exception class derived from
std::exception
that is thrown from a compiled library and which is copyable, the exception can be caught and handled like any other bound C++ object (or with
Exception
on the Python and
std::exception
on the C++ side). If the exception is not copyable, but derived from
std::exception
, the result of its
what()
reported with an instance of Python’s
Exception
. In all other cases, including exceptions thrown from interpreted code (due to limitations of the Clang JIT), the exception will turn into an instance of
Exception
with a generic message.
The standard C++ exceptions are explicitly not mapped onto standard Python exceptions, since other than a few simple cases, the mapping is too crude to be useful as the typical usage in each standard library is too different. Thus, for example, a thrown
std::runtime_error
instance will become a
cppyy.gbl.std.runtime_error
instance on the Python side (with Python’s
Exception
as its base class), not a
RuntimeError
实例。
The C++ code used for the examples below can be found
here
, and it is assumed that that code is loaded at the start of any session. Download it, save it under the name
features.h
, and load it:
>>> import cppyy >>> cppyy.include('features.h') >>>
In addition, the examples require the
throw
to be in compiled code. Save the following and build it into a shared library
libfeatures.so
(或
libfeatures.dll
on MS Windows):
#include "features.h" void throw_an_error(int i) { if (i) throw SomeError{"this is an error"}; throw SomeOtherError{"this is another error"}; }
And load the resulting library:
>>> cppyy.load_library('libfeatures') >>>
Then try it out:
>>> cppyy.gbl.throw_an_error(1) Traceback (most recent call last): File "<stdin>", line 1, in <module> cppyy.gbl.SomeError: void ::throw_an_error(int i) => SomeError: this is an error >>>
Note how the full type is preserved and how the result of
what()
is used for printing the exception. By preserving the full C++ type, it is possible to call any other member functions the exception may provide beyond
what
or access any additional data it carries.
To catch the exception, you can either use the full type, or any of its base classes, including
Exception
and
cppyy.gbl.std.exception
:
>>> try: ... cppyy.gbl.throw_an_error(0) ... except cppyy.gbl.SomeOtherError as e: # catch by exact type ... print("received:", e) ... received: <cppyy.gbl.SomeOtherError object at 0x7f9e11d3db10> >>> try: ... cppyy.gbl.throw_an_error(0) ... except Exception as e: # catch through base class ... print("received:", e) ... received: <cppyy.gbl.SomeOtherError object at 0x7f9e11e00310> >>>