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.
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
At last, we can make the two executables and run them.
% make all
% ./runC2Cxx
Hello from C++!
% ./runC2F90
Hello from Fortran 90!