
----- Original Message ----- From: "David Abrahams" <dave@boostpro.com> To: <boost@lists.boost.org> 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-... 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