Boost.Python - <boost/python/errors.hpp>

###  | # Boost.Python ## Header <boost/python/errors.hpp>

## Contents

Introduction
Classes
  Class `error_already_set`
    Class `error_already_set` synopsis
Functions
  handle_exception
  expect_non_null
  throw_error_already_set
Examples

## Introduction

`<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.

## Classes

### Class `error_already_set`

`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.

#### Class error_already_set synopsis

    namespace boost { namespace python
    {
        class error_already_set {};
    }}

## Functions

    template <class T> bool handle_exception(T f) throw();

    void handle_exception() throw();

Requires: 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).
Effects: 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.
Postconditions: No exception is being handled
Throws: nothing
Rationale: 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);

Returns: `x`
Throws: `error_already_set()` iff `x == 0`.
Rationale: Simplifies error-handling when calling functions in the Python/C API which return 0 on error.

    void throw_error_already_set();

Effects: `throw error_already_set();`

Rationale: 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.

## Examples

    #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;
       }
    }

Revised 13 November, 2002

© Copyright Dave Abrahams 2002.