A complete Hello World client

Exception handling: more C mapping

Until now, in all the code fragments I showed, there were some CORBA_Environment variables lying around with some CORBA_exception_init calls... Let's see what these variables are about.

This is no news: there is no support in C for exception handling. One has to create complete systems to support signals and the like (look at GTK+ signal system). CORBA has such a system. Its use is really simple. Each method has a CORBA_Environment * parameter which is used to store the exceptions' data. If, during a method execution an exception is raised, the fact that the exception occurred and the exception data is stored in this structure. Here it is:

typedef struct CORBA_Environment {

        CORBA_exception_type _major;

        /* .... */
        
} CORBA_Environment;

The _major field indicates whether an exception was raised or not during method invocation. It may have 3 values: CORBA_NO_EXCEPTION, CORBA_USER_EXCEPTION or CORBA_SYSTEM_EXCEPTION. Their signification is rather clear. Typically, good code should test after each method invocation if an exception occurred with:

CORBA_Environment ev;

if (ev->_major == CORBA_NO_EXCEPTION)
        /* goooood :) */
else
        /* baaaaad :( */
      

We have also a nice set of functions to operate on these exceptions. We can throw exceptions with CORBA_exception_set. CORBA_exception_id returns a string to clearly identify the exception. CORBA_exception_value returns a pointer to the exception structure and CORBA_exception_free is used to reinitialize the ev structure after an exception. Here are their signatures:

extern CORBA_char *CORBA_exception_id (CORBA_Environment *ev);

extern void *CORBA_exception_value (CORBA_Environment *ev);

extern void CORBA_exception_free (CORBA_Environment *ev);

extern void CORBA_exception_set (CORBA_Environment *ev, 
                                CORBA_exception_type major,
                                CORBA_char *exception_id,
                                void *parameters);
      

And here is some sample code which uses the 3 first functions:

// here is the interface we wish to access
module FruitsBasket {
interface Apple {
        exception eaten {
                string<40> why;
        };
        void EatMe () raises (eaten);  
};
};
#include <orb/orbit.h>
/* here, code to access the interface */
int main (int argc, char *argv)
{
        CORBA_Object obj;
        CORBA_Environment ev;
        CORBA_ORB orb;

        CORBA_exception_init (&ev);
        orb = CORBA_ORB_init (&ev);

        if (ev->_major != CORBA_NO_EXCEPTION) {
                fprintf (stderr, 
                         "Error: unable to initialize the ORB: %s\n", 
                         CORBA_exception_id (&ev));
                CORBA_exception_free (&ev);
                exit (1);
        } 

        /* code to link to an Apple object */

        /* note that a switch /case statement would 
         * have been cleaner but i am sick of all this code ...
         */
        FruitsBasket_Apple_EatMe (orb, &ev);
        if (ev->_major != CORBA_NO_EXCEPTION) {
                if (ev->_major == CORBA_USER_EXCEPTION) {
                        CORBA_char *buffer;
                        buffer = CORBA_exception_id (&ev);
                        if (strcmp (buffer, "FruitsBasket_Apple_eaten")) {
                                fprintf (stderr, 
                                         "user exception: Apple already eaten: %s\n", 
                                         (CORBA_exception_value (&ev))->why);
                                exit (1);
                        } else {
                                fprintf (stderr, 
                                         "unknown exception raised!! Error!!\n");
                                exit (2);
                        } 
                } else {
                        fprintf (stderr, 
                                 "System exception: %s\n", 
                                 CORBA_exception_id (&ev));
                        exit (1);
                }
        }
        return 0;
}

Finally, to throw exceptions from your code, you must pass to CORBA_exception_set the correct parameters: the CORBA_exception_type must be either CORBA_USER_EXCEPTION or CORBA_SYSTEM_EXCEPTION. The CORBA_char must be the exception name #defined in the headers and the void * a pointer to an exception structure where fields are filled and where memory is allocated with __alloc(). Here is a small example using the same Apple object:

/* here, we are in the server and we wish to throw 
 * the "eaten" exception with the correct "why" 
 * reason.
 * note that our Apple Object exception will map 
 * to the following C structure:
typedef struct FruitsBasket_Apple_eaten {
        CORBA_char why[40];

        /* ... plus miscellaneous implementation specific members */

} FruitsBasket_Apple_eaten;
#define ex_FruitsBasket_Apple_eaten <identifier>
FruitsBasket_Apple_eaten *FruitsBasket_Apple_eatme__alloc ()
*/
FruitsBasket_Apple_eaten *exception;

exception = FruitsBasket_Apple_eaten__alloc ();

strcpy (exception->why, "mmmm...");

CORBA_exception_set (&ev, CORBA_USER_EXCEPTION,
                        ex_FruitsBasket_Apple_eaten,
                        (void *) exception);
      

Commented code

Now that exception handling has no secret for you anymore, you may look at this hello world code easily. We will use the following interface definition:

interface Echo {
        void echo (in string<40> str);
};
      

And here is the code:

main () 
{
        CORBA_ORB orb;
        CORBA_Environment ev;
        CORBA_Object obj;
        FILE *file;
        /* rather long string: should be enough for all our needs :) */
        char buffer[10000]; 
        char *ior;

        CORBA_exception_init(&ev);
        orb = CORBA_ORB_init(&argc, argv, "orbit-local-orb", &ev);
        if (ev->_major != CORBA_NO_EXCEPTION) {
                fprintf (stderr, 
                         "Error: exception during ORB init: %s\n", 
                         CORBA_exception_id(&ev));
                exit (1);
        }

        if (!file = fopen ("object.ior", "r")) {
                fprintf (stderr, 
                         "error: could not open serializing file\n");
                exit (2);
        }

        fgets (buffer, 10000, file);
        ior = g_strdup (buffer); /* gets the raw string */

        /* now, we have it finally !! */
        obj = CORBA_ORB_string_to_object (orb, ior, &ev);
        if (ev->_major != CORBA_NO_EXCEPTION) {
                fprintf (stderr, 
                         "Error: could not get object reference: %s\n", 
                         CORBA_exception_id(&ev));
                exit (3);
        }
        fclose (file);

        Echo_echo (obj, ,&ev);
        if (ev->_major != CORBA_NO_EXCEPTION)
                fprintf (stderr, 
                         "Error: could not get method: %s\n", 
                         CORBA_exception_id(&ev));

        CORBA_exception_free(&ev);

        /* now, we could call many other objects... */
}

In fact, this code is nothing but code we already seen, with exception handling code added.