The rationale for this step is that both om and the ob to be developed for Use Case 3 share some common features, so they should also share common code where possible. In particular, both programs will generate a log file that has a name derived from the name of the program and which contains a timestamped list of messages. Also, both programs receive datagrams that contain requests, which are strings that are in the general format:
<request type> <argument> ...Both om and ob use the <request type> (sometimes called the keyword)part of a request to select a request processing function to be called, and pass a string consisting of all the <argument>s separated by spaces to the function. The request processing function returns another string, called the reply. (A client sends a request in a datagram to om or ob, and om or ob sends the reply in another datagram to the client.)
The requirement being introduced here is to implement log file management and request processing function management in separately compiled source modules.
Put functions prototypes for your log file management functions in a
header file, put their definitions (implementations) in a
.cc
file, and adjust your Makefile so that building
om links the object file for the main program with the object
file for the log file management functions. Be sure to
#include
the header file in both the source modules. Here
is what you might have in your Makefile, assuming you have defined the
OM_OBJS
and LDFLAGS
macros properly:
om : $(OM_OBJS) <tab> gcc $(OM_OBJS) $(LDFLAGS) -o om
for (;;) {
1. Receive a request in a datagram.
2. Parse the datagram into two strings, the <request
type> and the list of <argument>s.
3. Search for the <request type> in your dispatch
table, and call the request processing function, passing
it the second string (the list of <argument>s) as
its argument.
4. If the <request type> was not in the
dispatch table, send a datagram containing the string
"*** unrecognized request ***"
to the
client.
5. If the <request type> was in the dispatch
table, send a datagram containing the string returned by
the request processing function to the client.
}
Put the code for Steps 2 and 3 of this pseudocode into a separate source
file. You might have one function or two; I think a single function is
easier to manage. It would receive the datagram as an argument and
would return either the "*** unrecognized request ***"
string or whatever string was returned by the request processing
function. Put the function prototype for this function (these
functions) in a separate header file, and #include
this
header file in both your main program's source file and the source file
containing the function's (functions') definition(s).
What else you do for managing request processing functions depends on
how you implemented your dispatch table. If you used an array of
structs as described in class, you need to make the array global to
both the main .cc
file and the one that contains the
function(s) just described. The way to manage that is to declare the
array as an extern
in the header file, and to provide the
initialization of the array in (either) one of the .cc
files. Here is some sample code. (You must use meaningful
function and variable names!)
fun.h | om.cc | fun.cc |
---|---|---|
typedef char *rpf(char *) rpf do_exit; rpf do_listClasses; struct dt { char *n; rpf *f; }; extern dt tab[]; // Prototype for parser/dispatcher char *p_a_d(char*); | #include "fun.h" dt tab[] = { {"exit", do_exit }, {"listClasses", do_listClasses}, }; ... // Step 2 of above algorithm recvfrom ( ... &d ... ); // Steps 2 & 3 of above algorithm r = p_a_d(d); // Steps 4 & 5 of above algorithm sendto( ... r ... ); | #include "fun.h" char* p_a_d(char *k_a) { // Break k_a into k and a ... // Find k in tab[] ... // Pass a to the processing function // and return whatever it returns return tab[i].f(a); ... } |
.cc
files, or in yet another one. (Don't forget to
#include
the header file if you use another one.)
If you decide instead to use an object-oriented approach, you would
have a class that holds the dispatch table and which has member
functions for adding items to the table and for searching the table for
a particular request type string. In this implementation, the class
declaration goes in the header file, and the class method definitions
go in a separate .cc
file. You might reasonably decide to
implement the dispatch table as a linked list if you follow this
approach.
In either case, you will now need to update the definitions of
OM_OBJS
in your Makefile, and you will have to be sure to
update the dependencies for each object module to include the proper header
files:
depend : $(OM_SRCS) $(HDRS) <tab> makedepend $(OM_SRCS)