#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 { 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; }; template class handler_chain : 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_chain() {} 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_chain& 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( f, e, &Derived::catch_ ); } template void try_impl( callable const& f , optional& e , R (Derived::*)(X) const ) const { typedef remove_reference::type exception_type; try { this->try_nested( f, e ); } catch( exception_type& 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_chain 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; } }