#include "boost/python.hpp"
#include <stdexcept>
#include <iostream>
/**
* Content:
* * example, which explain how to create custom exception class, which gives
* expected behaviour to exceptions exposed using Boost.Python library.
*
* The example also allows you to map your exception classes to the Python
* built-in ones.
*
**/
class application_error : public std::exception{
public:
application_error()
: std::exception(), m_msg()
{}
application_error( const std::string& msg )
: std::exception(), m_msg( msg )
{}
application_error( const application_error& other )
: std::exception(other), m_msg( other.m_msg )
{}
const std::string& message() const
{ return m_msg; }
virtual ~application_error() throw(){}
std::string application_name() const
{ return "my_exceptions module"; }
private:
const std::string m_msg;
};
//small dummy function that will conditionaly throws exception
void check_preconditions( bool raise_error ){
if( raise_error ){
throw application_error( "xyz" );
}
}
//test function for custom r-value converter
std::string get_application_name( const application_error& err ){
return err.application_name();
}
namespace bpl = boost::python;
struct exception_translator{
exception_translator(){
bpl::register_exception_translator<application_error>(&exception_translator::translate);
//Register custom r-value converter
//There are situations, where we have to pass the exception back to
//C++ library. This will do the trick
bpl::converter::registry::push_back( &exception_translator::convertible
, &exception_translator::construct
, bpl::type_id<application_error>() );
}
static void
translate( const application_error& err ){
bpl::object pimpl_err( err );
bpl::object pyerr_class = pimpl_err.attr( "py_err_class" );
bpl::object pyerr = pyerr_class( pimpl_err );
PyErr_SetObject( pyerr_class.ptr(), bpl::incref( pyerr.ptr() ) );
}
//Sometimes, exceptions should be passed back to the library.
static void*
convertible(PyObject* py_obj){
if( 1 != PyObject_IsInstance( py_obj, PyExc_Exception ) ){
return 0;
}
if( !PyObject_HasAttrString( py_obj, "_pimpl" ) ){
return 0;
}
bpl::object pyerr( bpl::handle<>( bpl::borrowed( py_obj ) ) );
bpl::object pimpl = bpl::getattr( pyerr, "_pimpl" );
bpl::extract<application_error> type_checker( pimpl );
if( !type_checker.check() ){
return 0;
}
return py_obj;
}
static void
construct( PyObject* py_obj, bpl::converter::rvalue_from_python_stage1_data* data){
typedef bpl::converter::rvalue_from_python_storage<application_error> storage_t;
bpl::object pyerr( bpl::handle<>( bpl::borrowed( py_obj ) ) );
bpl::object pimpl = bpl::getattr( pyerr, "_pimpl" );
storage_t* the_storage = reinterpret_cast<storage_t*>( data );
void* memory_chunk = the_storage->storage.bytes;
application_error* cpp_err
= new (memory_chunk) application_error( bpl::extract<application_error>(pimpl) );
data->convertible = memory_chunk;
}
};
BOOST_PYTHON_MODULE( my_exceptions ){
typedef bpl::return_value_policy< bpl::copy_const_reference > return_copy_const_ref;
bpl::class_< application_error >( "_application_error_" )
.def( bpl::init<const std::string&>() )
.def( bpl::init<const application_error&>() )
.def( "application_name", &application_error::application_name)
.def( "message", &application_error::message, return_copy_const_ref() )
.def( "__str__", &application_error::message, return_copy_const_ref() );
exception_translator();
bpl::def( "check_preconditions", &::check_preconditions );
bpl::def( "get_application_name", &::get_application_name );
}