Using ACE Exception Handling Macros to Enhance CORBA Portability

CORBA Environment arguments provide a way to handle exceptions when native c++ exception handling is unavailable or undesirable. However, writing portable code using both native C++ exception handling and CORBA::Environment objects is very hard. If you plan to write portable code that must run on platforms that do not have native C++ exceptions, therefore, we recommend you use the ACE exception macros. This document explains how these macros can help alleviate much of the accidental complexity. However, keep in mind macros cannot solve all problems perfectly.

Before reading the rest of this document, we recommend you check out pages 307 through to 322 in the book, Advanced Corba Programming with C++ by Michi Henning & Steve Vinoski. Likewise, we recommend that you read the Error Handling chapter from the TAO Developer's Guide.


Table of Contents


ACE Exception Macros in a Nutshell

This section explains some simple rules of writing programs for platforms with and without native exception support using ACE's exception macros.

ACE exception macros are modelled like C++ language exceptions and can be used like them, but with a small difference. These macros rely on the CORBA::Environment variable to handle exceptions on platforms that do not support exception handling. (Please note that native exceptions can be turned on or off at COMPILE time as an option to your make) The exception macros have been modelled with some extra rules to ensure this works even on platforms without native exception support. See some quick examples on how to use ACE exception macros.

  1. Declaration of CORBA::Environment Parameter at Methods
    On platforms lacking native exceptions, all CORBA methods take an extra parameter added at the end of their argument list. This last parameter holds the CORBA::Environment variable. However, on systems with native exceptions, no such extra method parameter is added.
    In order for both configurations to work with the same source code, following macros are defined. In native exception configurations, they all resolve to empty.


  2. Passing the CORBA::Environment Parameter into Method Calls
    Now that we've seen how to declare methods with Environment parameters, let's see how to invoke such methods.


  3. Definition of the CORBA::Environment variable
    We have seen how to declare methods with the CORBA::Environment parameter, and how to invoke such methods. However, where does the variable to pass into methods as the CORBA::Environment parameter come from in the first place?

    An environment variable can be defined in the needed scope (for example, in the main program, or in a more local scope) by the statement

          ACE_DECLARE_NEW_CORBA_ENV;
          

    You can then invoke the methods on the servant from the client side as

          object_reference->func_name (x, y  ACE_ENV_ARG_PARAMETER);
          
    Even if you are interested in making calls within the client side, you can define your method like this
          int AN_OBJ::foobar (int a, int b  ACE_ENV_ARG_DECL);
          
  4. Throwing exceptions:
    Use ACE_THROW and ACE_THROW_RETURN to throw exceptions. They should never be used within a try block; please use ACE_TRY_THROW instead.

  5. Propagating exceptions:
    To simulate native exceptions on platforms without native exception handling, every function call that may throw exceptions must be followed by ACE_CHECK or ACE_CHECK_RETURN.

    Exception-throwing functions include the following categories:

    1. Any function that takes a CORBA_Environment argument.

    2. ACE_NEW_THROW_EX. Notice that you should not use ACE_NEW_THROW, ACE_NEW_THROW_RETURN, ACE_NEW_TRY_THROW anymore because they don't work right with ACE try macros. Instead, use ACE_NEW_THROW with appropriate ACE_CHECK* macros.

    3. ACE_GUARD_THROW_EX, ACE_READ_GURAD_THROW_EX, and ACE_WRITE_THROW_EX.

    4. ACE_TRY blocks. Follow every ACE_ENDTRY with appropriate ACE_CHECK* macros.

    You should pass ACE_TRY_ENV to these functions.

    Be very careful not to combine exception throwing functions in one statement like this:

              x = obj1->callme (ACE_ENV_SINGLE_ARG_PARAMETER)
                + obj2->dare_me (ACE_ENV_SINGLE_ARG_PARAMETER);
              ACE_CHECK;
          

    This example may work differently when native exception handling is enabled/disabled.

  6. Catching exceptions:
    Use ACE_TRY to catch exceptions if there's an ACE_TRY_ENV available. Otherwise, you should use ACE_DECLARE_NEW_CORBA_ENV to create one at proper scope. The use of ACE_TRY_NEW_ENV is considered depricated because it can't deal with the case when you have multiple TRY blocks in the scope of ACE_TRY_NEW_ENV. If there are more than one try blocks in a function, use ACE_TRY_EX for all subsequence try blocks to avoid name clashing of labels.

  7. Printing out exceptions. Use ACE_PRINT_EXCEPTION (EX,INFO) to print out an exception. The macro takes two arguments, a reference to an exception (EX) and a char * string (INFO) which provides more information on the exception. Since there's no portable way to print out exceptions, you can redefine ACE_PRINT_EXCEPTION to fit your need (or define it to null.) You should always print out the exception itself, not the CORBA_Environment that carries the exception.

  8. Name of CORBA::Environment variable
    A function that may throw a CORBA::Exception needs a CORBA::Environment variable to pass up exceptions (to throw in the C++ sense) and to gather (catch () in the C++ sense) exceptions from functions it called. By default, ACE exception macros assume that the variable is named ACE_TRY_ENV. ACE_TRY_ENV itself is also a macro which can be redefined.

    You can redefine the name of the variable to something else to avoid name clashing. Alternatively, there's another macro (ACE_ADOPT_CORBA_ENV) that allow you to use another variable name as the default CORBA::Environment within a function.


Examples

Refer to $ACE_ROOT/ace/CORBA_macros.h for complete definitions of macros discussed here.

General Guidelines for Exception Handling


Transition from ACE_TRY_ENV usage to the ACE_ENV_ARG macros

Before TAO version 1.2.2, IDL defined methods were declared using direct mentions of CORBA::Environment ACE_TRY_ENV. The problem with this approach was that the ACE_TRY_ENV had to be passed into ORB core method calls even when native exceptions are supported. The TAO internal usage of the ACE_ENV_ARG family of macros fixes this.

For people who want to continue to use their old code that uses the old ACE_TRY_ENV macros, they can define ACE_ENV_BKWD_COMPAT in their config.h file.

CORBA applications that do not need support for emulated exceptions can use direct C++ exception handling and omit the CORBA::Environment parameter entirely.
On the other hand, applications that shall support environments without C++ exceptions (such as all applications that are part of to TAO itself) should use the ACE_ENV_ARG macros.
The script $ACE_ROOT/bin/subst_env.pl can assist in the conversion from the direct ACE_TRY_ENV usage to the ACE_ENV_ARG macros. Here is a list of the substitutions that the script does. For context, two sample IDL methods are used:

        void noargs ();
        void withargs (in boolean b);
At each example, first the old usage is given, then its subsitution.

Method declaration

Method invocation


Caveats

As we already mentioned no set of macros can cover all cases and preserve the semantics between native C++ exceptions and the CORBA::Environment based mapping. Some of the problems that our macros are described below:


Back to the ACE documentation page.
Back to ACE Documentation Home.