next up previous contents index
Next: Portable Makefiles for Static Up: Minimal Makefiles: static linked Previous: Writing the Fortran 90 Implementation   Contents   Index


Writing the C Client

Now, finally, we are ready to write a client. For this exercise, we wrote our driver in C and built two executables; each one linking in one of the two implementation libraries. We will put our driver in the minimal/ directory (which happens to be the parent directory of where the C++ and Fortran 90 implementations are, though this detail is only relevant to makefile construction). From our Fortran 90 subdirectory, we go up one and generate the client-side C bindings.

% cd ..
% babel -cC ../hello.sidl

The ``-cC'' flag, or its equivalent long-form ``-client=C'', tells the Babel code generator to create only the C stub calling code, not the entire library implementation.

There are a few details worth noting here. The C bindings generate function names by combining packages, classes, and method names with underscores (e.g. Hello_World_getMsg(). Whenever you see double underscores in Babel generated symbols, they indicate something built-in to (and sometimes specific to) the language binding. The _create() method is built-in to every instantiatable class defined in SIDL, triggering the creation of Babel internal data structures as well as the constructor of the actual object implementation.

The code listing below shows a well crafted driver with full error checking.


   1 #include <stdio.h>
   2 #include "Hello_World.h"
   3 #include "sidl_BaseInterface.h"
   4 #include "sidl_Exception.h"
   5 
   6 
   7 int main() { 
   8   Hello_World h;
   9   sidl_BaseInterface ex;
  10   char * msg;
  11 
  12   /* create instance of Hello World */
  13   h = Hello_World__create(&ex); SIDL_CHECK(ex);
  14   if ( h == NULL ) { 
  15     fprintf(stderr,"%s:%d Failed to create an instance of Hello_World!\n",
  16             __FILE__,__LINE__);
  17     return 2;
  18   }
  19 
  20   /* get the message from the object */
  21   msg = Hello_World_getMsg(h, &ex); SIDL_CHECK(ex);
  22   if ( msg == NULL ) { 
  23     fprintf(stderr, "%s:%d Hello_World_getMsg() returned a NULL instead "
  24 	            "of a string!\n",__FILE__,__LINE__);
  25     return 3;
  26   }
  27   
  28   /* done with object so we can release it */
  29   Hello_World_deleteRef(h,&ex); SIDL_CHECK(ex);
  30 
  31   /* print the string */
  32   printf("%s\n",msg);
  33 
  34   /* release the string */
  35   sidl_String_free(msg);
  36 
  37   return 0;
  38   
  39  EXIT: /* this is error handling code for any exceptions that were thrown */
  40   {
  41     fprintf(stderr,"%s:%d: Error, exception caught\n",__FILE__,__LINE__);
  42     sidl_BaseInterface ignore = NULL;
  43     sidl_BaseException be = sidl_BaseException__cast(ex,&ignore);
  44 
  45     msg = sidl_BaseException_getNote(be, &ignore);
  46     fprintf(stderr,"%s\n",msg);
  47     sidl_String_free(msg);
  48 
  49     msg = sidl_BaseException_getTrace(be, &ignore);
  50     fprintf(stderr,"%s\n",msg);
  51     sidl_String_free(msg);
  52 
  53     sidl_BaseException_deleteRef(be, &ignore);
  54     SIDL_CLEAR(ex);
  55     return 1;
  56   }
  57 }

As with other examples, we will go through this one line by line. It is important to note that no where in this file is any indication of what language the Babel object is implemented in. When you see the makefile, we will show that this code can be linked against multiple implementations in different languages.

line 2:
This line includes the C stub for the Hello.World type.
line 3:
We also include the C stub for the sidl.BaseInterface type which all classes and interfaces ultimately inherit from. In C, we often use this type to hold the exception argument.
line 4:
There is no sidl.Exception type. This header actually introduces some useful macros for dealing with exception handling in C.
line 13:
This is where the object is _create()'ed. Note that the creation may fail, so we use the SIDL_CHECK macro introduced from sidl_Exception.h to test the exception and goto EXIT (line 39), if necessary.
line 21:
With a live reference to the object, we now try to get the message out of it. Note that we check if the exception is thrown and if the string is NULL.
line 29:
Once we have the message, we can dispose of our reference to the object.
line 32:
Print the message
line 35:
Free the string. Return values have the same semantics as out parameters which is the caller always recieves a reference count and is obligied to dispose of it when done.
line 37:
Normal termination.
lines 39-56:
This is exception handling code. Its hard to imagine so many possibilities for failure in our little example, but it is useful to see how exception classes can be cast to appropriate types (line 43), and be queried for both original error message and the trace of the call stack from which it was thrown.
line 42:
Note that Babel generated methods always throw exceptions, but in exception handling code, we often ignore them. Do not call SIDL_CHECK after the EXIT as this can easily result in an infinite loop.

Now we need to write a makefile that builds the code in this directory and links it with the C++ or Fortran 90 implementations in the two subdirectories.


   1 # A minimal makefile for Fortran 90 Babel Impl
   2 # Assumes babel-config is in the current path
   3 # Assumes babel -cC ../hello.sidl is already run
   4 
   5 include babel.make
   6 
   7 LIBDIR=`babel-config --libdir`
   8 LIBS=-lsidlstub_cxx -lsidlstub_f90 -lsidl
   9 OBJS = main.o ${STUBSRCS:.c=.o} 
  10 
  11 all: runC2Cxx runC2F90
  12 
  13 .SUFFIXES:
  14 .SUFFIXES: .c .o
  15 
  16 .c.o:
  17 	gcc `babel-config --includes` -c $< 
  18 
  19 runC2Cxx: ${OBJS} libCxx/libhello.a
  20 	g++ -o runC2Cxx $(OBJS) libCxx/libhello.a \
  21 		-L$(LIBDIR) `babel-config --libs-f90` $(LIBS)
  22 
  23 runC2F90: ${OBJS} libF90/libhello.a
  24 	g++ -o runC2F90 $(OBJS) libF90/libhello.a \
  25 		-L$(LIBDIR) `babel-config --libs-f90` $(LIBS)
  26 
  27 .PHONY:  clean new
  28 
  29 clean:
  30 	${RM} *.o *~ babel.make.depends babel.make.package
  31 
  32 new: clean
  33 	$(RM) $(IORHDRS) $(STUBSRCS) $(STUBHDRS) runC2Cxx runC2F90 

line 5:
Again we include the Babel-generated makefile fragment. Again we see that its contents depend on the language being generated.
line 6:
We use babel-config to find out what directory Babel was configured to put its libraries in.
line 7:
We create a makefile macro to link in some of Babel's default libraries. libsidl.{a,so} is the Babel runtime library. All Babel code ultimately depends on this because it includes the implementation of sidl.BaseClass. libsidlstub_cxx is a library of C++ stubs and libsidlstub_f90 is a library of F90 stubs to libsidl.
line 8:
Since client side only includes Stubs and IOR.h, there is much less to compile.
line 11:
Default for this makefile is to create two executables.
lines 19-25:
Note that we are using the C++ compiler as our linker. This is generally the safest bet because if there's any C++ template code in your combined application, chances are the C++ compiler will want to do template instantiation at link time. babel-config can provide all the library flags that the Fortran 90 compiler silently passes on to the linker.

At last, we can make the two executables and run them.

% make all
% ./runC2Cxx
Hello from C++!
% ./runC2F90
Hello from Fortran 90!


next up previous contents index
Next: Portable Makefiles for Static Up: Minimal Makefiles: static linked Previous: Writing the Fortran 90 Implementation   Contents   Index


babel-1.0.0
users_guide Last Modified 2006-07-21

http://www.llnl.gov/CASC/components
components@llnl.gov