Boost logo

Boost :

Subject: Re: [boost] [Exception] Why is there no non-const version ofget_error_info?
From: Emil Dotchevski (emildotchevski_at_[hidden])
Date: 2009-08-20 14:12:12


>From your description, it seems the first thing you need to consider
is what is the exception safety guarantees of the function that
notifies the listeners, as a whole.

1) If only basic exception safety guarantee is required, then you
simply don't worry about the exceptions: if a onNotify throws, it'll
propagate the exception out.

2) If strong exception safety guarantee is required, then you must
make sure that if a onNotify throws, the effects of the already
completed onNotifys can be undone without an error. Then, you
propagate the exception as in 1).

3) Nothrow exception safety guarantee: you know onNotify won't throw.

It seems you're leaning towards 3). If that's the case, if an onNotify
throws, you have a bug. Therefore I don't think it is a good idea at
all to "keep going". If I were you, I'd just catch(...) and assert(),
possibly after logging
boost::current_exception_diagnostic_information() somewhere.

That said, if you want to attach a bunch of exception_ptrs to a
boost::exception, you could just use different tags:

typedef boost:error_info<struct onNotifyFoo_,boost::exception_ptr> onNotifyFoo;
typedef boost:error_info<struct onNotifyBar_,boost::exception_ptr> onNotifyBar;

Alternatively, you can collect them all into a
std::vector<boost::exception_ptr>. When you're done calling onNotifys,
if the vector is not empty you just throw an exception, adding the
vector to it.

If you are concerned about the (remote?) possibility of a
std::bad_alloc propagating instead of your exceptions, you shouldn't
be because that could happen anyway. To throw an exceptoin, the
runtime needs memory to store the exception object. In some
implementations that memory comes from the heap, so an attempt to
throw any exception whatsoever could result in a std::bad_alloc
instead (the runtime is required to have enough memory to throw a
std::bad_alloc.)

Emil Dotchevski
Reverge Studios, Inc.
http://www.revergestudios.com/reblog/index.php?n=ReCode

On Wed, Aug 19, 2009 at 11:03 PM, Adam Badura<abadura_at_[hidden]> wrote:
>> I'm curious, could you post more information about your use case?
>> What's this vector, what kind of objects/info does it store? I'm not
>> against your request, but I want to understand your motivation better.
>
>   In our system we use Observer pattern through Observable and Listener
> classes. Now Observable in its core function "notify" iterates through all
> registered Listeners and calls their "onNotify" function. This is quite
> simple. However it gets complicated once we start to take exceptions into
> account.
>   (Generally Listener's onNotify should not throw. This is motivated with
> two reasons: 1) it should be simple and fast, 2) object which caused the
> notification most likely cannot handle the exception anyway. Or at least
> such are popular guidelines for implementing Observer pattern. However in my
> opinion this is inconvenient [especially if one wants to be correct
> according to Standard and must in result take into account that almost any
> standard function may throw almost anything]. It is hard to write and
> maintain such "onNotifys" and I'm afraid that it might be in general not
> possible at all. So we do take into account exceptions thrown from
> "onNotify".)
>   Now consider a case where there are 3 Listeners. "notify" executes. First
> Listener has its "onNotify" called and does just fine. But second one
> throws. There is no way to somehow revert the "onNotify" already executed on
> the first Listener. So if we just propagate the exception we will end in
> incorrect state since first Listener was already notified, second is in
> state dependent on the exception and third was not notified at all.
>   My idea was to catch any exceptions thrown from "onNotifys", store them in
> the vector and then rethrow a different exception (for now called
> MultiException) which would contain that vector as its data.
>   This makes it much harder to catch the exception since now to catch
> exception X you will have to catch exception X and MultiException to see if
> it contains X. On the other hand exceptions from "notify" will not usually
> be handled anyway and the exception data serves only diagnostic purposes. So
> I do not consider it a major issue.
>   My first design was to do code like:
>
>   typedef boost::error_info<
>       SubExceptionsInfoTag,
>       std::vector< boost::exeption_ptr >
>   > SubExceptionsInfo;
>
>   // Does not throw.
>   MultiException exceptionObject;
>   // May throw. We haven't started notifying yet so no problem.
>   exceptionObject << SubExceptionsInfo( std::vector< boost::exeption_ptr >()
> );
>   // Does not throw.
>   // THIS IS NOT POSSIBLE BECAUSE CURRENTLY THE RETURNED POINTER IS CONST.
>   std::vector< boost::exception_ptr >* pExceptionsVector =
>       boost::get_error_info< SubExceptionsInfo >( exceptionObject );
>   // Prepare memory for worst case: every Listener throws.
>   // May throw. We haven't started notifying yet so no problem.
>   pExceptionsVector->reserve( m_listenersList.size() );
>
>   // We are ready with exceptionObject. Start actual notifications.
>
>   for each listener in listenersList
>   {
>       try
>       {
>           call onNotify on the listener
>       }
>       catch ( ... )
>       {
>           // Store the exception and continue the loop.
>
>           // current_exception does not throw.
>           // push_back will not throw since:
>           // 1) memory is already reserved,
>           // 2) boost::exeption_ptr copy constructor does not throw.
>           pExceptionsVector->push_back( boost::current_exception() );
>       }
>   }
>
>   // If any exception was thrown then throw exceptionObject.
>   if ( !pExceptionsVector->empty() )
>       // exceptionObject copy constructor does not throw.
>       throw exceptionObject;
>
>
>   With get_error_info returning a const pointer this no longer is possible.
> The closes approach would be to just have a simple vector with pre-reserved
> memory and then after the loop we will construct MultiException add that
> vector to it and throw it. However this may fail twice: adding vector to the
> exception and copying the vector. If this fails then a new exception will be
> thrown and original exception data will be lost. And I think it is bad
> because it can be quite easily written so that it either fails before
> notifying or fails only with onNotify exceptions.
>
>   Adam Badura
>
> _______________________________________________
> Unsubscribe & other changes:
> http://lists.boost.org/mailman/listinfo.cgi/boost


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