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 -makefile -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 edit the GNUmakefile that builds the code in this directory and links it with the C++ or Fortran 90 implementations in the two subdirectories. This case requires more editing that the previous two examples.
1 include babel.make 2 # please name the server library here 3 LIBNAME=client 4 # please name the SIDL file here 5 SIDLFILE=../hello.sidl 6 # extra include/compile flags 7 EXTRAFLAGS= 8 # extra libraries that the implementation needs to link against 9 EXTRALIBS= 10 # library version number 11 VERSION=0.1.1 12 # PREFIX specifies the top of the installation directory 13 PREFIX=/usr/local 14 # the default installation installs the .la and .scl (if any) into the 15 # LIBDIR 16 LIBDIR=$(PREFIX)/lib 17 # the default installation installs the stub header and IOR header files 18 # in INCLDIR 19 INCLDIR=$(PREFIX)/include 20 21 22 # most of the rest of the file should not require editing 23 24 ifeq ($(IMPLSRCS),) 25 SCLFILE= 26 BABELFLAG=--client=c 27 MODFLAG= 28 else 29 SCLFILE=lib$(LIBNAME).scl 30 BABELFLAG=--server=c 31 MODFLAG=-module 32 endif 33 34 all : lib$(LIBNAME).la $(SCLFILE) runC2Cxx runC2F90 35 36 CXX=`babel-config --query-var=CXX` 37 runC2Cxx: lib$(LIBNAME).la libCxx/libhello.la main.lo 38 babel-libtool --mode=link $(CXX) -static main.lo lib$(LIBNAME).la \ 39 libCxx/libhello.la -o runC2Cxx 40 41 runC2F90: lib$(LIBNAME).la libF90/libhello.la 42 babel-libtool --mode=link $(CC) -static main.lo lib$(LIBNAME).la \ 43 libF90/libhello.la -o runC2F90 44 45 CC=`babel-config --query-var=CC` 46 INCLUDES=`babel-config --includes` 47 CFLAGS=`babel-config --flags-c` 48 LIBS=`babel-config --libs-c-client` 49 50 STUBOBJS=$(STUBSRCS:.c=.lo) 51 IOROBJS=$(IORSRCS:.c=.lo) 52 SKELOBJS=$(SKELSRCS:.c=.lo) 53 IMPLOBJS=$(IMPLSRCS:.c=.lo) 54 55 PUREBABELGEN=$(IORHDRS) $(IORSRCS) $(STUBSRCS) $(STUBHDRS) $(SKELSRCS) 56 BABELGEN=$(IMPLHDRS) $(IMPLSRCS) 57 58 $(IMPLOBJS) : $(STUBHDRS) $(IORHDRS) $(IMPLHDRS) 59 60 lib$(LIBNAME).la : $(STUBOBJS) $(IOROBJS) $(IMPLOBJS) $(SKELOBJS) 61 babel-libtool --mode=link --tag=CC $(CC) -o lib$(LIBNAME).la \ 62 -rpath $(LIBDIR) -release $(VERSION) \ 63 -no-undefined $(MODFLAG) \ 64 $(CFLAGS) $(EXTRAFLAGS) $^ $(LIBS) \ 65 $(EXTRALIBS) 66 67 $(PUREBABELGEN) $(BABELGEN) : babel-stamp 68 @if test -f $@; then \ 69 touch $@; \ 70 else \ 71 rm -f babel-stamp ; \ 72 $(MAKE) babel-stamp; \ 73 fi 74 75 babel-stamp: $(SIDLFILE) 76 @rm -f babel-temp 77 @touch babel-temp 78 babel $(BABELFLAG) $(SIDLFILE) 79 @mv -f babel-temp $@ 80 81 lib$(LIBNAME).scl : $(IORSRCS) 82 ifeq ($(IORSRCS),) 83 echo "lib$(LIBNAME).scl is not needed for client-side C bindings." 84 else 85 -rm -f $@ 86 echo '<?xml version="1.0" ?>' > $@ 87 echo '<scl>' >> $@ 88 if test `uname` = "Darwin"; then scope="global"; else scope="local"; \ 89 fi ; \ 90 echo ' <library uri="'`pwd`/lib$(LIBNAME).la'" scope="'"$$scope"'" resolution="lazy" >' >> $@ 91 grep __set_epv $^ /dev/null | awk 'BEGIN {FS=":"} { print $$1}' | sort -u | sed -e 's/_IOR.c//g' -e 's/_/./g' | awk ' { printf " <class name=\"%s\" desc=\"ior/impl\" />\n", $$1 }' >>$@ 92 echo " </library>" >>$@ 93 echo "</scl>" >>$@ 94 endif 95 96 .SUFFIXES: .lo 97 98 .c.lo: 99 babel-libtool --mode=compile --tag=CC $(CC) $(INCLUDES) $(CFLAGS) $(EXTRAFLAGS) -c -o $@ $< 100 101 clean : 102 -rm -f $(PUREBABELGEN) babel-temp babel-stamp *.o *.lo 103 104 realclean : clean 105 -rm -f lib$(LIBNAME).la lib$(LIBNAME).scl 106 -rm -rf .libs 107 108 install : install-libs install-headers install-scl 109 110 111 install-libs : lib$(LIBNAME).la 112 -mkdir -p $(LIBDIR) 113 babel-libtool --mode=install install -c lib$(LIBNAME).la \ 114 $(LIBDIR)/lib$(LIBNAME).la 115 116 install-scl : $(SCLFILE) 117 ifneq ($(IORSRCS),) 118 -rm -f $(LIBDIR)/lib$(LIBNAME).scl 119 -mkdir -p $(LIBDIR) 120 echo '<?xml version="1.0" ?>' > $(LIBDIR)/lib$(LIBNAME).scl 121 echo '<scl>' >> $(LIBDIR)/lib$(LIBNAME).scl 122 if test `uname` = "Darwin"; then scope="global"; else scope="local"; \ 123 fi ; \ 124 echo ' <library uri="'$(LIBDIR)/lib$(LIBNAME).la'" scope="'"$$scope"'" resolution="lazy" >' >> $(LIBDIR)/lib$(LIBNAME).scl 125 grep __set_epv $^ /dev/null | awk 'BEGIN {FS=":"} { print $$1}' | sort -u | sed -e 's/_IOR.c//g' -e 's/_/./g' | awk ' { printf " <class name=\"%s\" desc=\"ior/impl\" />\n", $$1 }' >>$(LIBDIR)/lib$(LIBNAME).scl 126 echo " </library>" >>$(LIBDIR)/lib$(LIBNAME).scl 127 echo "</scl>" >>$(LIBDIR)/lib$(LIBNAME).scl 128 endif 129 130 install-headers : $(IORHDRS) $(STUBHDRS) 131 -mkdir -p $(INCLDIR) 132 for i in $^ ; do \ 133 babel-libtool --mode=install cp $$i $(INCLDIR)/$$i ; \ 134 done 135 136 .PHONY: all clean realclean install install-libs install-headers install-scl
At last, we can make the two executables and run them.
% make all
% ./runC2Cxx
Hello from C++!
% ./runC2F90
Hello from Fortran 90!