Boost logo

Boost :

From: vicente.botet (vicente.botet_at_[hidden])
Date: 2008-08-29 00:21:46


----- Original Message -----
From: "David Abrahams" <dave_at_[hidden]>
To: <boost_at_[hidden]>
Sent: Wednesday, August 27, 2008 8:13 PM
Subject: [boost] [exception][policy] (was: Should we provide hot fixes?)

>
>
> on Wed Aug 27 2008, "Robert Ramey" <ramey-AT-rrsd.com> wrote:
>>
>> I'm referring specificly to the episode regarding boost exception
>> where by the purpose of boost::throw exception was changed
>> from a limited, lightweight piece of code crafted to address the
>> simple problem that some compilers don't implement "throw"
>> to a heavy weight (includes a bunch of new headers) module
>> whose purpose is totally inpenetrable and adds RTTI requirement
>> to any library which used boost::throw exception.
>
> This does look like a problem to me.

Hi,

I also think that this was a bad decision. When we use boost::throw we don't
want to pay for the case the exception must be transfered to another thread.
This must be paid only on the current_exception function, when we want to
transfer to another thread. This should be done using a specific
boost::exception_ptr::throw function.

The main problem comes from the fact exception_ptr current_exception() do
not works for any exception. So Emil has found that boost::throw could be
used to throw a boost::exception wrapping the intendeed exception. IMHO, the
exception_ptr emulation (without language support) to transport exceptions
between threads is not of real use if current_exception() do not works for
any exception. If we were able to ensure that current_exception manage all
exceptions we dont will need that boost::throw wrappes a exception on a
exception_ptr.

As Anthony has said some compilers (as MSVC) allows to recover the current
exception, so for this compilers should not be a problem when Emil introduce
this in the Exception library. For the other compilers we need a mechanism
that allows the user to configure the exceptions that needs to be wrapped
with a boost::exception_ptr as the Exception library do already for the STL
exceptions.

A few months ago I proposed an intrusive way (using the preprocesor)
allowing the user to define which exceptions will be wrapped by
boost::exception_ptr (see
http://www.nabble.com/Re%3A--Boost-users---Boost.thread--Exception-handling-strategy---td18572922.html#a18572922
for details).

I want to propose now another approach using a chain of responsabilities.
The trick consists in replacing the catch(...) handler which currently
returns a exception_detail::current_exception_unknown_exception() by a user
specific one.

inline exception_ptr
current_exception()
{
    try
    {
        throw;
    }
    // ... as before
    catch( ... )
    {
        return (*boost::user_current_exception_handler)();
    }
}
The default user_current_exception_handler will return a
exception_detail::current_exception_unknown_exception() as it is done now.

The user current exception handlers have the responsability to return a
exception_ptr wrapping the exception managed, and call to the next exception
handler on the catch(...).

The exception library needs to provide a way to set this
user_current_exception_handler returning the old one.

inline exception_ptr (*)() set_user_current_exception_handler(exception_ptr
(*new_handler)() ) {
    exception_ptr (*old_handler)() = boost::user_current_exception_handler;
    boost::user_current_exception_handler=new_handler;
    return old_handler;
}

Evidently this function is not thread_safe.

Then a user can define its own current_exception_handler as follows:

exception_ptr X_current_exception_handler() {
    try
    {
        throw;
    }
    // catch my own exceptions
    catch(X_exception_1 & e ) {
        return exception_detail::current_exception_std_exception(e);
    }
    // ...
    catch(X_exception_k & e ) {
        return exception_detail::current_exception_std_exception(e);
    }
    catch( ... )
    {
        return (*X_next_current_exception_handler)();
    }
}

Let me continue with the initialization of the
X_next_current_exception_handler. This can be done in a simple way on the
main thread

X_next_current_exception_handler=set_user_current_exception_handler(my_current_exception_handler);

or we can think in a more elaborated way, using static initialization. As
static initialization is done sequentially, there is no problem with the non
thread safety of the set_user_current_exception_handler. But I don't know
how to ensure that the boost::user_current_exception_handler is initialized
before the user ones other than including all the user next current
exception handler in the same compilation unit as the
boost::user_current_exception_handler.

The Exception library could define a type that do the set of the next
handler at construction time.

class exception_handler_participant {
    exception_ptr (*next_)() ;
public:
    exception_handler_setter(exception_ptr (*new_handler)()) {
        next_ = set_user_current_exception_handler(new_handler);
    }
    exception_ptr next() {
        return (*next_)();
    }
};

Each library/application can declare its own current exception handler
participant in a header file and define it in a specific _def.cpp file wich
will be included by the application.

// X_current_exception_handler_participant_def.cpp
// include whathever is needed
static const boost::exception::current_exception_handler_participant
X_current_exception_handler_participant(X_current_exception_handler);

and define its handler as
exception_ptr X_current_exception_handler() {
    try
    {
        throw;
    }
    // catch my own exceptions
    catch(X_exception_1 & e ) {
        return exception_detail::current_exception_std_exception(e);
    }
    // ...
    catch(X_exception_k & e ) {
        return exception_detail::current_exception_std_exception(e);
    }
    catch( ... )
    {
        return X_current_exception_handler_participant.next();
    }
}

The final user will include all the participants definitions in a unique
file in order to ensure the static initialization order.

// final_user_current_exception_handler_participants_def.cpp

#include <boost/exception/user_current_exception_handler_def.cpp>
#include <Alib_current_exception_handler_participant_def.cpp>
#include <Blib_current_exception_handler_participant_def.cpp>
#include <X_current_exception_handler_participant_def.cpp>

The runtime approach is more time consuming than the preprocesor one, but do
not have the preprocesor limitation that do not works when the function
using the current_exception is declared in a file that is not compiled by
the final user, i.e. it is contained in a static or dynamic library.

There is yet a limitation: the behavior of calling current_exception on the
constructor of a statically variable is undefined because we can not ensure
that the current_exception_handler_participants static variables have been
initialized. But this seams to me a minor limitation.

What do you think?

Vicente


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk