...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
<boost/python/errors.hpp> provides types and functions for managing and translating between Python and C++ exceptions. This is relatively low-level functionality that is mostly used internally by Boost.Python. Users should seldom need it.
error_already_set is an exception type which can be thrown to indicate
that a Python error has occurred. If thrown, the precondition is that
PyErr_Occurred()
returns a value convertible to true
.
Portable code shouldn't throw this exception type directly, but should
instead use throw_error_already_set(),
below.
namespace boost { namespace python { class error_already_set {}; }}
template <class T> bool handle_exception(T f) throw(); void handle_exception() throw();
The first form requires that the expression function0<void>(f) is valid. The second form requires that a C++ exception is currently being handled (see section 15.1 in the C++ standard).
The first form calls f() inside a try block which first attempts to use all registered exception translators. If none of those translates the exception, the catch clauses then set an appropriate Python exception for the C++ exception caught, returning true if an exception was thrown, false otherwise. The second form passes a function which rethrows the exception currently being handled to the first form.
No exception is being handled
nothing
At inter-language boundaries it is important to ensure that no C++ exceptions escape, since the calling language usually doesn't have the equipment necessary to properly unwind the stack. Use handle_exception to manage exception translation whenever your C++ code is called directly from the Python API. This is done for you automatically by the usual function wrapping facilities: make_function(), make_constructor(), def() and class_::def(). The second form can be more convenient to use (see the example below), but various compilers have problems when exceptions are rethrown from within an enclosing try block.
template <class T> T* expect_non_null(T* x);
x
error_already_set() iff x == 0.
Simplifies error-handling when calling functions in the Python/C API which return 0 on error.
void throw_error_already_set();
throw error_already_set();
Simplifies error-handling when calling functions in the Python/C API which return 0 on error.
void throw_error_already_set();
throw error_already_set();
Many platforms and compilers are not able to consistently catch exceptions thrown across shared library boundaries. Using this function from the Boost.Python library ensures that the appropriate catch block in handle_exception() can catch the exception.
#include <string> #include <boost/python/errors.hpp> #include <boost/python/object.hpp> #include <boost/python/handle.hpp> // Returns a std::string which has the same value as obj's "__name__" // attribute. std::string get_name(boost::python::object obj) { // throws if there's no __name__ attribute PyObject* p = boost::python::expect_non_null( PyObject_GetAttrString(obj.ptr(), "__name__")); char const* s = PyString_AsString(p); if (s != 0) Py_DECREF(p); // throws if it's not a Python string std::string result( boost::python::expect_non_null( PyString_AsString(p))); Py_DECREF(p); // Done with p return result; } // // Demonstrate form 1 of handle_exception // // Place into result a Python Int object whose value is 1 if a and b have // identical "__name__" attributes, 0 otherwise. void same_name_impl(PyObject*& result, boost::python::object a, boost::python::object b) { result = PyInt_FromLong( get_name(a) == get_name(a2)); } object borrowed_object(PyObject* p) { return boost::python::object( boost::python::handle<>( boost::python::borrowed(a1))); } // This is an example Python 'C' API interface function extern "C" PyObject* same_name(PyObject* args, PyObject* keywords) { PyObject* a1; PyObject* a2; PyObject* result = 0; if (!PyArg_ParseTuple(args, const_cast<char*>("OO"), &a1, &a2)) return 0; // Use boost::bind to make an object compatible with // boost::Function0<void> if (boost::python::handle_exception( boost::bind<void>(same_name_impl, boost::ref(result), borrowed_object(a1), borrowed_object(a2)))) { // an exception was thrown; the Python error was set by // handle_exception() return 0; } return result; } // // Demonstrate form 2 of handle_exception. Not well-supported by all // compilers. // extern "C" PyObject* same_name2(PyObject* args, PyObject* keywords) { PyObject* a1; PyObject* a2; PyObject* result = 0; if (!PyArg_ParseTuple(args, const_cast<char*>("OO"), &a1, &a2)) return 0; try { return PyInt_FromLong( get_name(borrowed_object(a1)) == get_name(borrowed_object(a2))); } catch(...) { // If an exception was thrown, translate it to Python boost::python::handle_exception(); return 0; } }