[cppx] Exception translation, part I

MFC throws pointers, Xerces throws various types not derived from std::exception, and as I recall the Lotus Notes API throws integers. An interesting diversity. But as the Chinese curse goes: may you live in interesting times! Such non-standard exceptions generally require you to have multiple catches every place such an exception could otherwise escape to higher levels, or where you want to handle any exception with a given general meaning. It’s not that the designers have tried to be clever or that they’re out to get you: it’s just that these libraries stem from before C++ was standardized, i.e., before 1998.

One partial remedy is exception translation. Somehow arrange for any non-standard exception to be caught and result in some corresponding exception derived from std::exception. It does not solve all the problems – but it sure beats doing it by copy-n’-paste coding of exception handlers!

With C++98 exception translation cannot be completely centralized in a portable, reusable way. As far as I know that’s not possible even with the upcoming C++0x standard. But it’s possible to provide portable and application-independent support that does most of the job, and that provides a general convention that largely eliminates the chance of any non-standard exception slipping through, and that’s what I discuss here.

Existing support for exception translation

Perhaps the most well known C++ exception translation scheme is the Visual C++ _set_se_translator function. But that scheme does not translate from non-standard C++ exceptions to standard C++ exceptions. It translates from Windows API “structured exceptions”, usually referred to as SEH exceptions, to C++ exceptions.

And perhaps the least known exception translation scheme is the C++98 library’s unexpected() exception translation callback. But first of all it offers only “after the fact” translation, when an unexpected exception attempts to propagate out of a function with an exception specification. And secondly not all modern C++ compilers support this. In particular, Microsoft’s Visual C++ does not support it (except that MSVC accepts the exception specification syntax and silently ignores it). Third, exception specifications will be removed or will at least be deprecated in C++0x. So in practice unexpected() is not an answer, to anything.

I do not know of any 3rd party library solutions except the one I’m presenting here. But I haven’t googled. Since the cppx library is intended to not depend on other libraries except the standard library, at least to the degree practically possible!, it would have to offer this functionality anyway.

Catch-throw translation

If you have ever used more than one non-standard-exception library in the same code, chances are that you coded at least one instance of the catch-throw exception translation idiom, like …

The catch-throw exception translation idiom (complete example):

#include <progrock/cppx/collections/iterator_range_util.h>
#include <iostream>
#include <string>
#include <stdexcept>
#include <assert.h>
#include <stdlib.h>     // EXIT_SUCCESS, EXIT_FAILURE

void throwX( std::string const& s ) { throw std::runtime_error( s ); }

struct X666 {};
struct MS666 {};
void xercesFunc()   { throw X666(); }           // Non-standard class.
void mfcFunc()      { throw new MS666; }        // Pointer to new'ed object.
void notesFunc()    { throw 666; }              // Integer.

// In actual code the use of lib funcs would be hardwired, not argument.
void oneOfMyLibraryUsingFuncs( void libFunc() )
{
    using namespace std;
    static std::string const xPrefix    = "oneOfMyLibraryUsingFuncs: ";

    // The catch-throw exception translation idiom:
    try
    {
        libFunc();          // The rather tiny payload, the business code.
    }
    catch( X666 const& )
    {
        throwX( xPrefix + "Xerces exception" );
    }
    catch( MS666* pX )
    {
        delete pX;
        throwX( xPrefix + "MFC exception" );
    }
    catch( int )
    {
        throwX( xPrefix + "Notes exception" );
    }
    catch( std::logic_error const& x )
    {
        cerr << "!!!" << xPrefix << x.what() << endl;
        assert( "std::logic_error exception indicating a bug" && false );
        std::terminate();
    }
    catch( std::exception const& x )
    {
        throwX( xPrefix + x.what() );   // Translates to std::runtime_error.
    }
    catch( ... )
    {
        cerr << "!!!" << xPrefix << "Unknown exception type" << endl;
        assert( "Unknown exception type" && false );
        std::terminate();
    }
}

// Just a driver, it doesn't matter what it does:
int main()
{
    using namespace progrock::cppx;
    using namespace std;

    static void (*const libFuncs[])()   =
    {
        &xercesFunc, &mfcFunc, &notesFunc
    };

    for( Index i = 0;  i < size( libFuncs );  ++i )
    {
        try
        {
            oneOfMyLibraryUsingFuncs( libFuncs[i] );
            cout << "Success!" << endl;
            return EXIT_SUCCESS;
        }
        catch( std::exception const& x )
        {
            cerr << "!" << x.what() << endl;
        }
    }
    cout << "Sorry, all 3 attempts to do the Thing failed." << endl;
    return EXIT_FAILURE;
}

The catch-throw idiom coded inline may be acceptable for a single routine like oneOfMyLibraryUsingFuncs above, but with the same boilerplate exception handling code in many routines this starts getting really awkward and verbose, not to mention unmaintainable.

One quick-n’-dirty solution is then to define a macro that expands to the four or five or six catch clauses that turn out to be common in your application. And I’ve done that. But such a macro (1) is a macro, and (2) is application-specific: it cannot be directly reused in applications using a different mix of libraries. And (3) it doesn’t support any custom cleanup action. About the only thing it has going for it is that it’s maximally efficient for the task, but we’re talking about the case where an exception occurs, which is the case where some overhead is acceptable; this is a main assumption of a modern C++ compiler.

Catch-rethrow-catch-throw exception translation

Just to get rid of the macro (a worthy goal in itself!), and to support custom cleanup actions, you can let a separate routine rethrow the exception and translate it. I.e. the full sequence is then a catch-rethrow-catch-throw. This avoids possible name clashes, reduced readability, non-debuggability and other macro problems, plus that it gives you a warm cozy feeling, at the cost of some slightly reduced efficiency when an exception is thrown by the called library code:

Catch-rethrow-catch-throw exception translation:

void rethrowAsStandardException( std::string const& prefix )
{
    using namespace std;

    try
    {
        throw;
    }
    catch( X666 const& )
    {
        throwX( prefix + "Xerces exception" );
    }
    catch( MS666* pX )
    {
        delete pX;
        throwX( prefix + "MFC exception" );
    }
    catch( int )
    {
        throwX( prefix + "Notes exception" );
    }
    catch( std::logic_error const& x )
    {
        cerr << "!!!" << prefix << x.what() << endl;
        assert( "std::logic_error exception indicating a bug" && false );
        std::terminate();
    }
    catch( std::exception const& x )
    {
        throwX( prefix + x.what() );    // Translates to std::runtime_error.
    }
    catch( ... )
    {
        cerr << "!!!" << prefix << "Unknown exception type" << endl;
        assert( "Unknown exception type" && false );
        std::terminate();
    }
}

// In actual code the use of lib funcs would be hardwired, not argument.
void oneOfMyLibraryUsingFuncs( void libFunc() )
{
    // The catch-rethrow-catch-throw exception translation idiom:
    try
    {
        libFunc();          // The rather tiny payload, the business code.
    }
    catch( ... )
    {
        // You can place some custom cleanup action(s) here.
        rethrowAsStandardException( "oneOfMyLibraryUsingFuncs: " );
    }
}

The rest of the program, in particular main, as before, with same effect as before.

I don’t know who invented this technique, but I recall that I read about it in the [comp.lang.c++] Usenet group and tried it with MSVC 6.0, and that must have been in the middle 1990’s. It did not work with MSVC 6.0, due to a compiler bug. With MSVC 6.0 the rethrowing would cause an exception object’s destructor to be called twice (happily Microsoft has fixed this particular bug so that the above works, although a related or pretty similar destructor call bug, no call instead of double call, surfacing in somewhat different circumstances, is reportedly still present in MSVC 10).

Using the cppx exception translation support

The same example rewritten to use the cppx library’s exception translation support can look like this:

A routine using the cppx exception translation support:

// In actual code the use of lib funcs would be hardwired, not argument.
void oneOfMyLibraryUsingFuncs( void libFunc() )
{
    // Direct use of cppx exception translation:
    try
    {
        libFunc();          // The rather tiny payload, the business code.
    }
    catch( ... )
    {
        cppx::rethrowAsStdX( "oneOfMyLibraryUsingFuncs: " );
    }
}

Uh… Where is the translation specified? Well, I chose to show just the library using routine first, because otherwise you would probably get a Wrong Impression™.

The oneOfMyLibraryUsingFuncs routine is just one of a possibly great many such library using routines in an application. And with this approach each such routine can be happily unaware of the exception translation that is in effect. All that each routine has to do is to invoke the translation, by using a catch(...) and calling cppx::rethrowAsStdX.

As with the previous section’s program there is some fixed per-application code that defines the translation, but unlike the previous section’s program this is now reusable code – or at least it can be, but for illustration I here chose to handle two exception types in one translator routine, just to make it clear that that is also a possibility:

A routine using the cppx exception translation support:

#include <progrock/cppx/exception/throwing.h>
#include <progrock/cppx/collections/iterator_range_util.h>
#include <iostream>
#include <assert.h>
#include <stdlib.h>     // EXIT_SUCCESS, EXIT_FAILURE

using namespace progrock;

struct X666 {};
struct MS666 {};
void xercesFunc()   { throw X666(); }           // Non-standard class.
void mfcFunc()      { throw new MS666; }        // Pointer to new'ed object.
void notesFunc()    { throw 666; }              // Integer.


// In actual code the use of lib funcs would be hardwired, not argument.
void oneOfMyLibraryUsingFuncs( void libFunc() )
{
    // Direct use of cppx exception translation:
    try
    {
        libFunc();          // The rather tiny payload, the business code.
    }
    catch( ... )
    {
        cppx::rethrowAsStdX( "oneOfMyLibraryUsingFuncs: " );
    }
}


// Just a driver, it doesn't matter what it does:

void xercesXTranslator( cppx::Rethrower& reThrower )
{
    try { reThrower.rethrow(); }
    catch( X666 const&)
    {
        cppx::throwX( reThrower.prefix() + "Xerces exception" );
    }
}

void mfcAndNotesXTranslator( cppx::Rethrower& reThrower )
{
    try { reThrower.rethrow(); }
    catch( MS666 const* pX )
    {
        delete pX;
        cppx::throwX( reThrower.prefix() + "MFC exception" );
    }
    catch( int )
    {
        cppx::throwX( reThrower.prefix() + "Notes exception" );
    }
}

void logicErrorHandler( char const description[] )
{
    std::cerr << "!!!" << description << "std::logic_error" << std::endl;
    assert( "std::logic_error exception indicating a bug" && false );
    std::terminate();
}
 
void unknownExceptionHandler( char const [] )
{
    std::cerr << "!!!" << "Unknown exception type" << std::endl;
    assert( "Unknown exception type" && false );
    std::terminate();
}

int main()
{
    using namespace std;
    static void (*const libFuncs[])()   =
    {
        &xercesFunc, &mfcFunc, &notesFunc
    };

    cppx::xTranslators().setLogicXHandler( logicErrorHandler );
    cppx::xTranslators().setUnknownXHandler( unknownExceptionHandler );
    cppx::xTranslators().add( xercesXTranslator );
    cppx::xTranslators().add( mfcAndNotesXTranslator );

    for( int i = 0;  i < cppx::size( libFuncs );  ++i )
    {
        try
        {
            oneOfMyLibraryUsingFuncs( libFuncs[i] );
            cout << "Success!" << endl;
            return EXIT_SUCCESS;
        }
        catch( std::exception const& x )
        {
            cerr << "!" << x.what() << endl;
        }
    }
    cout << "Sorry, all 3 attempts to do the Thing failed." << endl;
    return EXIT_FAILURE;
}

The calls to setLogicXHandler and setUknownXHandler are not strictly necessary. The default handling is to throw standard exceptions derived from respectively std::logic_error and std::runtime_error. But exceptions that denote bugs are generally troublesome, and with the calls this example works the same as the previous two examples.

Summary

In this posting I have discussed the general problem of non-standard exceptions, the catch-throw exception translation idiom, and the catch-rethrow-catch-throw idea of centralized non-macro exception translation. I presented an example using the basic cppx exception translation support. And I hope that that example (above) communicates the main ideas, although there’s more…. 🙂

I’m not sure what to discuss in part II, and whether there will also have to be a part III. That there is so much to discuss indicates to me that this is an area that is not really supported by the standard library. I wish it was: I think the lack of such support means that there is a lot of overly complex, redundant code Out There, and perhaps, because it’s so much work to do this kind of thing correctly without general support in place, perhaps there’s even incorrect code.

Anyway, – enjoy the ideas, implementation code to be discussed!

PS: Do feel free to comment. The cppx library is a Work In Progress, and chances are that you may have insights and ideas that I didn’t think of, and that are not well known. In a way this blog serves to communicate and generate ideas and practices, so discussion is very much welcome! 🙂

Advertisement

2 comments on “[cppx] Exception translation, part I

  1. Pingback: [cppx] Exception translation, part II | Alf on programming (mostly C++)

  2. One nice thing about blog postings is that you can go back and add reasoning to a posting. At least as long as no-one’s already commented! So I added the bit about cleanup actions, which I forgot to mention originally.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s