Using exceptions

VisIt uses exceptions for error handling. It has many types of exceptions, each of which ultimately inherit from "VisItException". The many types of exceptions can be found in "/src/common/Exceptions". Underneath that directory are specialized exceptions for databases, pipelines, plots, and visualization windows. If there are existing exceptions that fit the type of error you have, you are strongly encouraged to use that exception.

However, note that the inheritance hierarchy, which was meant to differentiate between broad classes of exceptions, is not being faithfully maintained. In addition, developers sometimes simply throw a "VisItException", without trying to identify it with one of the broad classes.

Throwing an exception

You can throw an exception with:

EXCEPTION2(BadIndexException, 5, 3);

This would throw an BadIndexException and pass the arguments "5" and "3" to the constructor of that exception. The "2" in EXCEPTION2 means that there are two arguments to the constructor.

This will also put a record of the exception in the debug stream .

Where an exception is caught (if you don't catch it)

Exceptions are typically caught by existing "safety code" to ensure non-fatal results. When executing a pipeline on the engine, the pipeline execution is aborted, the exception is caught, and the message generated by the exception is then sent back to the client. On the viewer and gui, exceptions are normally caught in the "right place" with the error then issued to the user. Of course, you should test this (by artificially triggering your exception) if you are on the gui or the viewer. About the only time you don't need to artificially trigger your exception call is if you are developing an avtFilter or avtFileFormat.

How to catch exceptions manually

Catches are also done with macros.

TRY
{
    ...
}
CATCH (BadIndexException)
{
    ...
}
CATCH2 (InvalidFilesException, e)
{
    ... // Do something with e
    // Rethrow the exception since we've handled 
    // what we can at this level.
    RETHROW; 
}
CATCHALL (...)
{
    ...
}
ENDTRY

A special catch macro must be used when returning from a TRY/CATCH block. The CATCH_RETURN macro must be used if you are returning from a TRY/CATCH block in order to keep fake exceptions working properly. The number passed to CATCH_RETURN indicates the number of TRY blocks that are in effect when the CATCH_RETURN macro is encountered.

void function()
{
    TRY
    {
       ...
    }
    CATCH(VisItException)
    {
       CATCH_RETURN(1);
    }
    ENDTRY
}

If your function returns a value then you can use CATCH_RETURN2 to return a value.

double function()
{
    TRY
    {
       ...
    }
    CATCH(VisItException)
    {
       CATCH_RETURN2(1, 3.14159);
    }
    ENDTRY
    return 0.;
}

Why macros are used

In the case of throwing exceptions, macros make sense, because they can cause exceptions to be thrown in a consistent way (always adding an entry to the debug file). However, macros are mostly used because some really old compilers don't support them sufficiently. The most common symptom of problems was when programs would crash when attempting to throw exceptions across shared library boundaries. In this case, exceptions can be simulated using setjmp/longjmp. You can enable this workaround by adding FAKE_EXCEPTIONS to your host.cmake's compilation flags. This problem rarely comes up anymore.

VisItException

VisItException is the base class of all exceptions for VisIt. As previously noted, if there isn't a good, existing candidate exception and you don't want to add a new exception, you can throw a VisItException.

EXCEPTION1(VisItException, "This is the string that will appear to users.")