Programming Guide


Exception Handling

 

This version of OpenDoc improves the handling of C++ and SOM exceptions. It provides better cross-platform and cross-compiler functions than the previous versions. It replaces the old header files, Except.h and Except.cpp, with the new header files, ODExcept.h and ODExcept.cpp. This version does not provide a DLL, because exception handling is dependent on your compiler. Compile and link the ODExcept.cpp code into your parts.

Note:

The files Except.h and Except.cpp are no longer shipped and should not be used.

Enhancements in this version of OpenDoc:

This version of OpenDoc provides support tools to augment the exception handling code. One important set of support tools is the debugging tools shipped as ODDebug.h. Include this header file in your code and define the DEBUG macro to use the debugging tools.

Guidelines for Coding Exception Handling

       

Using exceptions allows you to implement a useful C++ programming idiom to manage constructors and destructors of stack-based classes. The idiom simplifies the acquisition and release of resources such as memory blocks or references to a ref-counted object. You do not need to remember to code the release calls because it is implicit within the destructor code. When an object goes out of scope and any exceptions are returned, the release will take place on the way out. This is easier than wrapping an exception handler around the block.

The exception handling guidelines for OpenDoc are the same guidelines used to code exception handling in any C++-based SOM interface. OpenDoc adds no additional restrictions. These guidelines consider your part's needs to interoperate with DLLs that use the C++ runtimes from different compilers.

Use the New Header Files

     

All OpenDoc parts, extensions, and objects that implement SOM interfaces should include the ODExcept.h file and use the appropriate macros within it. Do not use the deprecated Except.h or Except.cpp header files. Do not use the deprecated services from the obsolete header. See "Obsolete Macros and Methods" for a list of the obsolete services.

Use the Debug Header Files

   

All OpenDoc parts, extensions, and objects that implement SOM interfaces should include the ODDebug.h header file and use the appropriate macros within it. You can disable the debugging messaging by modifying your makefiles. This modification will leave the debugging capabilities in your part's source code if correctly coded. See "Guidelines for Using the Debugging Tools." for additional details on the debugging tools.

Define the _NATIVE_EXCEPTIONS_ Macro

All OpenDoc parts, extensions, and objects should have the following macro defined:

          -D_NATIVE_EXCEPTIONS_    // as an nmake or compiler flag
or
    #define _NATIVE_EXCEPTIONS_ = 1  // in a header file

Never Throw a C++ Exception out of an OpenDoc Part DLL

Because you use SOM methods in an OpenDoc part, you must use SOM-style exceptions instead of C++ exceptions because:

Your part cannot throw a C++ exception outside its DLL. Your part is a compiled SOM object. In OpenDoc, it interoperates with other SOM DLLs. The other DLLs could be compiler with other C++ compilers. If the compiling of the DLLs of SOM objects is on different C++ compilers, there is a chance that exceptions returned by an object compiled in one DLL will be incompatible with an object compiled on a second DLL. When you have incompatible DLL files among objects, an exception returned by one object can be passed to another object causing one of the C++ runtimes to fail and the document shell or the part to crash.

If your code does not handle all the C++ exceptions from within its DLL, then the unhandled ones will pass out of your part across the SOM interface and could crash your system. To resolve possible problems, you can design your code to:

These code designs are to the outermost code of the SOM interface of your part. They serve as preparation in handling all C++ exceptions

For example, to change the flow of control within your part, you can wrap the return with code similar to:

  if (ev->_major) { return my_retcode }

For additional information on the ODSetSOMException macro, see "ODSetSOMException".

Note:

Never return a C++ exception from a SOM object. Catch the exception within that object, raise a SOM exception to the calling object, or raise a program assert.

Check for SOM Exceptions

When you call a method of another SOM object, you must prepare for and handle SOM exceptions than can be returned by the SOM object. Normally, the returned exception will be realized as a C++ exception of type ODException in your code. You can catch this exception by wrapping your code with the appropriate try and catch block.

There are times when an exception is not possible. Instead of a C++ exception, the calling object returns the SOM exception in the SOM environment. Your code should poll the SOM exception variable of the SOM environment on every invocation of the method.

Use ODSetSOMException

If your code handles an internal exception, C++ or otherwise, and needs to return a SOM exception to the caller, your code must use the ODSetSOMException macro to return immediately to the calling object.

Clear the SOM Exception Variable

If your code handles an internal exception, your code must use the SetErrorCode macro to clear the SOM exception value from the SOM environment. For additional information on the SetErrorCode macro, see "SetErrorCode".

SOM Compiler Flags

 

All of the .idl files for code that implement SOM interfaces must be processed with the SOM compiler. To handle exceptions correctly, the sc compiler flag -mchkexcept must be specified when compiling a .xh or .xih file.     This flag constructs the proper linkage between SOM exceptions and the C++ exceptions in the calling program.

This flag places an internal interface to resolve and invoke the correct instance of the called SOM object. The -m flag tells the SOM compiler to add return value checking code after each method resolution.     The added code tests the SOM environment for a SOM exception. If there is an exception, the code throw a C++ exception using the function named as a part of the flag. With OpenDoc, the function CHECK_ENV creates and raises the right type of OpenDoc exception.     Because the code is compiled into the calling C++ code, the same C++ runtime that your part uses is used for the exception. Therefore, the C++ exception is handled properly within your part.

Using SOMMalloc and SOMFree

     

Like exceptions, the memory management code of different C++ compiler runtimes use no standards. Programs cannot malloc() in a DLL on one compiler, pass the pointer to a DLL of another compiler, and then expect the free() to work in the second compiler. Using the SOM memory management code avoids this problem.

Programming Tools in ODExcept.h

   

The ODExcept.h header file provides programming tools. This section presents these tools by category and provides the syntax and semantics for each tool.

Tools to Raise Exceptions

     

The following tools are useful for raising exceptions. The definitions listed assume the standard OpenDoc C++ settings. The following macros return a C++ exception of type ODException.

The following macros return a C++ exception of type ODException and provide an additional message. The behavior of these macros changes depending on the value of the ODDebug debugging macro.

When debugging is disabled, no message is generated.

THROW
   

The syntax for the THROW macro is:

    void THROW (ODError error);

This macro is a function that returns void.

Throws a C++ exception of type ODException that holds the value specified in error. There is a list of standard OpenDoc error codes in the ErrorDef.xh header file.

Note:

You can use other values if they do not conflict. The error value must be nonzero.

THROW_IF_ERROR
   

The syntax for the THROW_IF_ERROR macro is:

    void THROW_IF_ERROR (ODError error);

This macro is a function that returns void.

Conditionally throws a C++ exception of type ODException that holds the value specified in error. There is a list of standard OpenDoc error codes in the header file ErrorDef.xh.

For example:

     THROW_IF_ERROR (myFunc(myParm))

Note:

You may use other values if they do not conflict. The error value is checked. The C++ exception is thrown if the error is nonzero.

THROW_IF_NULL
   

The syntax for the THROW_IF_NULL macro is one of the following:

    void THROW_IF_NULL (void* value);
or
    void THROW_IF_NULL (void* value,ODError error);

Both versions of this macro are functions that return void.

The first version of this macro conditionally throws a C++ exception of type ODException(kODErrOutOfMemory). The value is assumed to be a pointer and is checked for NULL. The C++ exception is returned if the pointer is null.

Note:

Because the error returned is an out of memory indicator, your code should avoid using this function for validating items other than pointers.

For example:

    THROW_IF_NULL (SOMMalloc(myptr))

The second version of this macro conditionally throws a C++ exception of type ODException(error).

Note:

Use this form of the macro if you are testing an item other than a pointer, because you are providing the type of error.

For example:

    THROW_IF_NULL (myFunc(myParm), kODMyError)

THROW_M
   

The syntax for the THROW_M macro is:

    void THROW_M (ODError error, const char* msg);

This macro is similar to THROW but with the addition of printing a message when the debugging tool is enabled. This message may or may not be in a popup message box, depending on which compiler you use.

THROW_IF_ERROR_M
   

The syntax for the THROW_IF_ERROR_M macro is:

    void THROW_IF_ERROR_M (ODError error, const char* msg);

This macro is similar to THROW_IN_ERROR but with the addition of printing a message when the debugging tool is enabled. This message may or may not be in a popup message box, depending on which compiler you use.

THROW_IF_NULL_M
   

The syntax for the THROW_IF_NULL_M macro is:

    void THROW_ID_NULL_M (void* value, const char* msg);

This macro is similar to THROW_IN_NULL but with the addition of printing a message when the debugging tool is enabled. This message may or may not be in a popup message box, depending on which compiler you use.

Note:

There is no overloaded version of this macro that takes the ODError parameter.

Tools to Manage the SOM Exception Handling Environment

     

The following tools are useful for managing the SOM environment and the current ODException structure in your code's exception handling blocks.

The basic programming interface is (not normally used by parts writers):

    void ODSetErrorCode (Environment *, ODException &, ODError);

Below are the macros that manage the value of the ODException structure named _exception in the current namespace. See the specific macros for additional information.

Note:

The macros assume the exception caught is called "_exception". You code should have a catch block similar to the following:
    try
     {...}
    catch(ODException _exception)
     {...}

ErrorCode
   

The syntax for the ErrorCode macro is:

            ErrorCode ()
    #define ErrorCode (_exception.error)

This macro is a function that returns the value of the error code in the current _exception variable. Use this macro within your catch block.

ErrorMessage
   

The syntax for the ErrorMessage macro is:

            ErrorMessage ()
    #define ErrorMessage (_exception.message)

This macro is a function that returns the value of the error message in the current _exception variable. Use this macro within your catch block.

SetErrorMessage
   

The syntax for the SetErrorMessage macro is:

            SetErrorMessage (msg)
    #define SetErrorMessage (strcpy(_exception.message, (msg)))

This macro sets the value of the error message in the current _exception variable. Use this macro within your catch block.

SetErrorCode
   

The syntax for the SetErrorCode macro is:

            SetErrorCode (err)
    #define SetErrorCode (ODSetErrorCode(kODNULL, _exception.message, (err)))

This macro sets the value of the error code in the current _exception variable. Use this macro within your catch block.

SetErrorCodeEv
   

The syntax for the SetErrorCodeEv macro is:

            SetErrorCodeEv (ev, err)
    #define SetErrorCodeEv (ODSetErrorCode(ev, _exception.message, (err)))

This macro sets the value of the error code in the current _exception variable. Use this macro within your catch block. Works on the on the exception attached to the global SOM environment that is supplied.

Tools to Get and Set Exception Values

       

The following are programming interfaces that get and set the ODError member or the ODException structure.

ODGetSOMException
   

The syntax of the ODGetSOMException macro is:

    ODError ODGetSOMException (Environment *ev );

This macro is a function that returns void.

The macro gets the value of the error part of the ODException structure and returns it to the caller.

ODSetSOMException
   

The syntax of the ODSetSOMException macro is:

   void ODSetSOMException (Environment*,
                            ODError,
                            const char *msg =kODNULL );
or
   void ODSetSOMException (Environment*, ODException& );

Both versions of this macro are functions that return void.

The first version of this macro creates a new exception structure and registers it with the SOM environment. It has default behavior for the value of the message string.

The second version of this macro creates a new exception structure and registers it with the SOM environment. It takes an ODException structure as the input.

For both versions, if you plan to raise a SOM exception, you should call this function first to create the exception you will raise.

Obsolete Macros and Methods

The following macros and methods are no longer used by OpenDoc. Your code will not work if it uses these deprecated services.

Note:

You should use the C++ keywords try and catch for the deprecated OpenDoc TRY and CATCH services.

Standard Structure for SOM Methods

     

Your code should use the standard C++ try and catch syntax. After your try blocks, you should provide as many catch blocks as necessary for the types of exceptions your code handles. If your code is a SOM method, it should always wrap the outermost scope of the method with a try block and it should always provide a complete, untyped catch at the end. The following code illustrates one form your code could take.
YourSOMType YourSOMMethod (YourSOMClass * somSelf,
                           Environment  * ev,
                           yourParms )
{
        // your SOM method
        try
        {
                // your primary logic
        }
        catch( ODException _exception ) // you must provide this one
        {
                // your SOM exception handling block
                SetErrorCodeEv(ev,err) // part of the error handling
                ...
                ODSetSOMException(ev,err) // to return exception thru SOM
        }
        catch( anothertype foo )       // you also handle this type
        {
                // your extra exception handling logic
        }
        catch( ... )                   // catches everything else
        {
                WARNMSG( )             // prints a warning
                assert(0)              // ANSI assert to really exit
        }
        if (ev->_major)
        {
                // the ODException error was set up already in a catch
                return yourError ;     // one way to handle this
        }
        // repeat the same try/catch/catch/catch block as needed
}

Basic Try and Catch Model

The following code is a basic example of the exception macros. It ignores some of the interactions with SOM and most of the setup. The focus is on the flow of exception handling. Assume that the principal function is the design as an interface of a SOM object.

This example, in two parts, shows a fail-safe allocation of two pointers. The first part of the example is a service function called MyAlloc. This function is C++ code compiled into your DLL. There is no interface from outside of the DLL. MyAlloc allocates memory and returns an exception if the memory allocation fails. THROW_IF_NULL is part of the exception handling service of ODExcept.h. For additional information, see "THROW_IF_NULL".
#include <ODExcept.h>
#include <ODDebug.h>
//-------------------------------
// the service function MyAlloc
// (only called within your own DLL)
//-------------------------------

void * MyAlloc(ODSize size)
{
    // not wrapped in try/catch due to an internal function
    void *    p;	
    p = malloc(size);

    THROW_IF_NULL(p);             // creates an ODException
                                  // that is returned by C++

    return p ;                    // only taken if no exception
}

The second part of the example is a function called foo. It uses the internal service function MyAlloc. It is designed to serve as a SOM interface. It shows the use of the coding guidelines presented in "Guidelines for Coding Exception Handling".

If the first call to MyAlloc fails, ODFoo returns to the caller with the error value -1. It handles the C++ exception internally, and then uses the return value rc as the channel of communication with the caller.

If the first call succeeds, control passes to the second call to MyAlloc, which is inside a second try and catch block. This block frees the p1 pointer, preventing a memory leak. After p1 is freed, the exception is handled, and the same rc mechanism is used to return to the caller.

If the second call to MyAlloc succeeds, we skip the second catch block. The second if test fails, and we return a zero for success.

If an unexpected C++ exception occurs, it enters the generic catch block at the bottom of the method. This block ensures that the C++ exception is not passed across a SOM object boundary, a DLL boundary, by asserting.
#include <ODExcept.h>
#include <ODDebug.h>
int ODFoo (ODFoo *somSelf, Environment * ev )
{
    // wrap everything in a big try block, to
    // catch all C++ exceptions
    try
    {
        void * p1 = 0 ;
        void * p2 = 0 ;
        int    rc = 0 ;
        // wrap the calls to a function which
        // can raise exceptions, so we can handle them
        try
        {
            p1 = MyAlloc(10000);
        }
        catch( ODException _exception )
        {
            cout<<"Failed to allocated p1"<<endl;
            SetErrorCodeEv( ev,kODErrOutOfMemory );
            rc=-1;
        }
        if( ev->_major ) { return rc; }
        try
        {
            p2 = MyAlloc(10000);
        }
        catch( ODException _exception )
        {
            cout<<"Failed to allocated p2"<<endl;
            SetErrorCodeEv( ev,kODErrOutOfMemory );
            free(p1);
            rc=-2;
        }
        if( ev->_major )
        {
            ODSetSOMException( ev, _exception );
            return rc;
        }
        return 0;
    }
    // overall catch block
    // this example code does not expect errors
    catch( ... )
    {
        if( p1 ) { free(p1) };
        if( p2 ) { free(p2) };
        // next two lines are workaround for ASSERT macros
        WARNMSG( WARN_INDEX(-1),"Unexpected C++ exception.\n" );
        assert( 0 );
    }
}

Automatic Environment Checking

     

Including the ODExcept.h header file defines a special preprocessor symbol that modifies the way SOM messages are sent. Any SOM headers (.xh files, for C++) compiled with the -mchkexcept flag of the SOM compiler     included after the header file has been included will be modified. After the message is sent and control returns to the caller, the SOM environment is checked and an exception of the type ODException return if the method returned an error.

Note:

The .xih and .xh header files shipped with OpenDoc were compiled with the -mchkexcept option.

The following example does not use autochecking.
#include <ODFoo.xh>
  ...
void AFunction(Environment *ev, ODFoo *foo)
{
  long result = foo->Bazz(ev);
  if( ev->_major )
  {
    // Oops, ODFoo::Bazz returned an error
    result = 0;
    goto handle_error;
  }
  ...
  handle_error:
  return result;
}

This code does not include ODExcept.h, leaving the automated exception checking disabled. AFunction must check the environment directly after each call.

The following example uses autochecking.
// Enables automatic ev checking for ODFoo
#include <ODExcept.h>
#include <ODFoo.xh>
  ...
long AFunction(Environment *ev, ODFoo *foo)
{
  long result;
  try
  {
    long result = foo->Bazz(ev);
  }
  catch( ODException _exception )
  {
    SetErrorCodeEv( ev , kODNoError );  // clear the error since we handled
    result = kODNULL;
  }
  ...
  return result;
}

Because ODExcept.h is included before ODFoo.xh, the environment checking code is added to the call to Bazz method. If Bazz encounters an error and returns error status in ev, a C++ exception with that same error code is returned. The error code is caught by the exception handler and is returned in the ev parameter.

Consider the following guidelines:

Guidelines for Using the Debugging Tools.

     

You can access the debugging tools by including the ODDebug.h and ODDebug.cpp files. The tools provide the macro categories:

Display Warnings

   

The following macros enable warnings from your OpenDoc code:

WARNMSG
   

This macro invokes a function that returns void.

The syntax of WARNMSG is:
WARNMSG (void *   catalogHandler,   // handle to an open catalog
               int      setIndex,   // set index in the catalog
               int      catalogIndex,     // message index in the catalog
         const char *   fileName,         // file name where warning is issued
         const int      lineNbr,          // line number in the file where
                                          //   warning was issued
               char *   defaultMsg,       // default message is not available
                                          //   from the catalog
               ...      /* per-message var args */ );

The catalog index accesses the IBM-supplied translated message catalog. The default message are used if problems occur with the catalog or index. WARNMSG is not affected by the DEBUG macro.

WARNMSG and WARNMSG_DEBUG macros use the catalogIndex and defaultmsg parameters for user-defined warning messages. If you do not have a message catalog, you can pass a value of -1 as the catalog index. Passing a -1 value forces the defaultMsg to be displayed as the message text.

WARNMSG_DEBUG
   

This macro has the same syntax as WARNMSG. The difference between the two macros is that code generated by the WARNMSG_DEBUG macro will not be compiled when DEBUG is not defined.

WARN_CATALOG
   

This macro supplies the standard parameters for the WARNMSG and WARNMSG_DEBUG macros. The definition of the WARN_CATALOG macro is:

    #define WARN_CATALOG (A,B,C) (void*) A,B,C, __FILE__ , __LINE__

WARN_CATALOG can be used with WARNMSG and WARN_DEBUG to produce the following code:

WARNMSG (
         WARN_CATALOG (catalogHandle, setIndex, catalogIndex),
         my_default_msg);

WARN_INDEX
   

This macro supplies the standard parameters for the WARNMSG and WARNMSG_DEBUG macros. The definition of the WARN_INDEX is:

    #define WARN_INDEX(A) NULL, NULL, (A), __FILE__, __LINE__

This macro is used chiefly by the OpenDoc runtime. It assumes your WARNMSG should use the ODMessage catalog, and the AMSG_SET in that catalog. However, if you do not have the ability to build a catalog, you may find using WARNMSG_INDEX (-1) the easiest way to put text in a message box.

Because WARNMSG and WARNMSG_DEBUG use the WARN_INDEX, you could use the following code:

    WARNMSG (                              // example with no parameters
            WARN_INDEX (kMyMsgNum),
            my_default_msg
            );
    WARNMSG (                         // example with parameters
            WARN_INDEX (kAnotherNum),
            my_default_msg,
            fMyVar1,
            fMyVar2
            );
    WARNMSG (                         // example with no message catalog
             WARN_INDEX (-1),
             my_default_msg
            );

Exceptions and Warning Based on Conditions

   

The following macros enable assertions from your OpenDoc code:

The macros ending with "_DEBUG" perform the same function, but they only generate code if you define the DEBUG macro.

ASSERTMSG and WASSERTMSG macros use the defaultMsg and index parameters for user-defined assertion messages. If you do not have a message catalog, use a value of -1 for the message index and place the message text in the default string.

Note:

The ASSERT macros use a built-in catalog of messages that is indexed by the ODError value supplied.

The ASSERT macros do not assert. These macros return C++ exceptions of type ODException. For a true assertion, your code must use the WARN* macros to generate a message, and then use the ANSI assert() of your C++ compiler as follows:
    WARNMSG (WARN_INDEX(-1), fmt, ... );    // your message
     assert (failing_condition );              // a real assert

ASSERT
   

The syntax of the ASSERT macro is :

    ASSERT (condition, ODError)

If the condition is false, a C++ exception is generated. The value of ODError is written to the thrown ODException structure. The OpenDoc code generates a popup that holds the translated message based on the value of the ODError.

ASSERTCATALOG
   

The syntax of the ASSERTCATALOG macro is:

    ASSERTCATALOG (condition, ODError, char *defaultMsg, catalogIndex,
                   catalogHandle, setIndex)

This macro is the same as ASSERT, but the additional message you supply comes from your message catalog.

ASSERTMSG
   

The syntax of the ASSERTMSG macro is :

    ASSERTMSG (condition, ODError, char *defaultMsg, index)

This macro is the same as ASSERT, but your code can supply an additional message accessed by the index. A default message will be used if the index or message catalog access fails. A C++ exception is returned.

WASSERT
   

The syntax of the WASSERT macro is :

    WASSERT (condition)

If the condition is false, the standard assertion message is printed without generating a C++ exception.

WASSERTCATALOG
   

The syntax of the WASSERTCATALOG macro is:

    WASSERTCATALOG (condition, char *defaultMsg, catalogIndex,
                    catalogHandle, setIndex)

The macro is the same as WASSERT, but the additional message you supply comes from your message catalog.

WASSERTMSG
   

The syntax of the WASSERTMSG macro is :

    WASSERTMSG (condition, char *defaultmsg, index)

This macro is the same as WASSERT, but you can supply and additional massage as described in ASSERTMSG.

ASSERT_NOT_NULL
   

The syntax of the ASSERT_NOT_NULL macro is :

    ASSERT_NOT_NULL (parm)

This special form of ASSERT tests the parm parameter against kODNULL and always generates the error kODErrIllegalNullInput.

Printing Messages

   

The PRINT macro enables printing and logging in your OpenDoc code. PRINT has the same syntax and semantics as somPrintf. PRINT is not affected by the DEBUG macro.    


[ Top | Previous | Next | Contents | Index | Documentation Homepage ]