C++ Boost

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.