[C++-sig] question on boost.python exception mechanisms

Holger Joukl Holger.Joukl at LBBW.de
Wed Apr 17 17:13:51 CEST 2013


Hi,

> From: "Niall Douglas" <s_sourceforge at nedprod.com>
> On 10 Apr 2013 at 13:48, Holger Joukl wrote:
>
> > > If you don't have C++11 in your C++, Boost provides an okay partial
> > > implementation of C++11 exception support.

I'm currently using gcc 4.6.1 which supports the necessary features using
-std=c++0x but also Solaris Studio 12.2 which doesn't so I'd need the
Boost implementation.

> > I'll look into this. This would then mean instrumenting some object
with a
> > place
> > to store the caught exception to re-raise upon reentry from C to C++.
> > I take it this would then do much the same as my approach sketched
above
> > but
> > "safer and sounder".
>
> There is always the trick of keeping a hash table keyed on thread id
> and call function depth. In fact it could even be made lock free and
> wait free with Boost v1.53's new lock and wait free queue
> implementations.

A bit of a simplified example version using Boost's partial C++11 exception
support
would then be s.th. like:

0 $ cat dispatch_main_exception_map.cpp
// File: dispatch_main_exception_map.cpp

#include <iostream>
#include <exception>
#include <stdexcept>
#include <pthread.h>
// header-only:
#include <boost/unordered_map.hpp>
// header-only:
#include <boost/exception_ptr.hpp>
#include "dispatch.h"


// the global per-thread exception storage
boost::unordered_map<pthread_t, boost::exception_ptr> exception_map;


void callback(cb_arg_t arg) {
    std::cout << "--> CPP callback" << arg << std::endl;
    std::cout << "<-- CPP callback" << arg << std::endl;
}


// this callback raises an exception
void callback_with_exception(cb_arg_t arg) {
    std::cout << "--> exception-throwing CPP callback" << arg << std::endl;
    throw std::runtime_error("throwing up");
    std::cout << "<-- exception-throwing CPP callback" << arg << std::endl;
}


// this callback raises an exception, catches it and stores it in the
// global (thread, exception)-map
void guarded_callback_with_exception(cb_arg_t arg) {
    std::cout << "--> guarded exception-throwing CPP callback" << arg <<
std::endl;
    try {
        throw std::runtime_error("throwing up");
    } catch (...) {
        std::cout << "storing exception in exception map" << std::endl;
        pthread_t current_thread = pthread_self();
        exception_map[current_thread] = boost::current_exception();
        exception_map.erase(current_thread);
        //global_exception_holder = boost::current_exception();
    }
    std::cout << "<-- guarded exception-throwing CPP callback" << arg <<
std::endl;
}


int main(void) {
    std::cout << "--> CPP main" << std::endl;
    cb_arg_t s = "CPP main callback argument";

    std::cout << std::endl;
    invoke(&callback, s);

    std::cout << std::endl;
    invoke(&guarded_callback_with_exception, s);
    pthread_t current_thread = pthread_self();
    if (exception_map.find(current_thread) != exception_map.end()) {
        try {
            std::cout << "rethrowing exception from exception map" <<
std::endl;
            boost::rethrow_exception(exception_map[current_thread]);
        } catch (const std::exception& exc) {
            std::cout << "caught callback exception: " << exc.what() <<
std::endl;
        }
    }

    std::cout << std::endl;
    try {
        invoke(&callback_with_exception, s);
    } catch (const std::exception& exc) {
        std::cout << "caught callback exception: " << exc.what() <<
std::endl;
    }

    std::cout << std::endl;
    std::cout << "<-- CPP main" << std::endl;
    return 0;
}

Which doesn't respect call function depth (how would I do that?)
and doesn't use a queue; I suppose you mean using the lockfree queue for
threadsafe
access to the hash table.

I think I don't even need that for my use case as I basically
- call dispatch on a Boost.Python wrapped object
- which invokes the C libs dispatcher function
- which invokes the registered callbacks (these will usually be implemented
in Python)

i.e. the same thread that produced the exception will need to handle it.

So what I have to make sure is that
- no exception can propagate back to the C lib
- I recognize an exception has happened, in the initial dispatch method in
the same
thread, to re-raise it and let Boost.Python translate it back to Python

It looks like getting at the thread ID is also not too portable. This is
something that
the simple solution of using (abusing?) the Python per-thread exception
indicator would
avoid.

Thanks for the valuable hints!

Holger

Landesbank Baden-Wuerttemberg
Anstalt des oeffentlichen Rechts
Hauptsitze: Stuttgart, Karlsruhe, Mannheim, Mainz
HRA 12704
Amtsgericht Stuttgart



More information about the Cplusplus-sig mailing list