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!