#include #include #include #include #include using namespace boost; // // callable/as_callable -- a lightweight substitute for // boost::function0; we don't need to copy the function object. // Could go further and use the boost::function trick to eliminate // virtual function bloat. // struct callable { virtual void operator()() const = 0; }; template struct as_callable : callable { as_callable( F f ) // implicit conversion from anything : f( f ) {} void operator()() const { f(); } F& f; }; // // A base class for nodes in the doubly-linked chain of individual // handlers that translate exceptions into objects of type Error. // template class handler_node : noncopyable { public: // Unlink from the chain. virtual ~handler_node() { next->prev = this->prev; prev->next = this->next; } protected: // Construct a doubly self-linked starter node handler_node() : prev( this ) , next( this ) {} // Construct and insert this node after chain handler_node( handler_node& chain ) : prev( &chain ) , next( chain.next ) { chain.next->prev = this; chain.next = this; } // Call f in the context of the next handler. void try_nested( callable const& f, optional& e ) const { assert( this->next != this ); return this->next->try_( f, e ); } private: // Call f within the context of this and all following handlers. // If an exception is handled, e will be assigned from the result // of the handler. Otherwise, the thrown exception propagates. virtual void try_( callable const& f, optional& e ) const = 0; private: // data members handler_node* prev; handler_node* next; }; // // The "starter node" for a doubly-linked list of handler_nodes. // template class handler_set : public handler_node { public: // Call f in the context of all handlers on this chain. If no // exception is thrown, the result is empty. Otherwise, if an // exception was handled the result is constructed from the result // of the handler. Otherwise, the thrown exception propagates. template optional operator()( F& f ) const { optional e; this->try_nested( as_callable( f ), e ); return e; } template optional operator()( F const& f ) const { optional e; this->try_nested( as_callable( f ), e ); return e; } // ------ // handler_set() {} private: virtual void try_( callable const& f, optional& ) const { // This is always the last node tried in any chain; we can // invoke f here. f(); } }; #include // A CRTP base class for exception handlers template < class Derived , class Error = void > class exception_handler : public handler_node { protected: exception_handler() {} exception_handler( handler_set& chain ) : handler_node( chain ) {} typedef exception_handler base; private: // handler_node override void try_( callable const& f, optional& e ) const { // To avoid having to specify the exception type as a template // parameter here, pass a pointer to the catch_ function and // allow the exception type to be deduced. return this->try_impl( &Derived::catch_, f, e ); } template void try_impl( R (Derived::*)(X) const , callable const& f , optional& e ) const { try { this->try_nested( f, e ); } catch( X x ) // Now that X is deduced, we can catch it. { // But don't use that member function pointer; we can call // the catch_ function directly. e = static_cast( this )->catch_( x ); } } }; // // Test code // handler_set translate; #include struct std_handler : exception_handler { std_handler() : base( translate ) {} std::string catch_( std::exception const& e ) const { return std::string( e.what() ); } } h1; #include struct int_handler : exception_handler { int_handler() : base( translate ) {} std::string catch_( int n ) const { return boost::lexical_cast( n ); } } h2; #include #include #include #include #include int main() { using namespace boost::lambda; if ( optional msg = translate( lambda::throw_exception( std::bad_alloc() ) ) ) { std::cout << *msg << std::endl; } if ( optional msg = translate( lambda::throw_exception( 42 ) ) ) { std::cout << *msg << std::endl; } // This one doesn't throw if ( optional x = translate( lambda::constant( "foobar" ) ) ) { std::cout << "unexpected error: " << *x << std::endl; } }