The OutRider Computing Journal

A free monthly column for users, administrators, programmers and hobbyists who use UNIXlike Operating Systems.

Home | Search | Site Map | Feedback | Subscriptions


NOTE: The intended audience for this paper is new programmers who wish to know how to create Object Oriented Like Programs in languages that may not have strong built in Object Oriented Methods (like Python or C++) or for Programmers who have only used Object Oriented Languages.

Modular Programming

Object Oriented Programming can actually be a pragma, and I might add, an especially good one to try to follow. I remember the very first time I heard of the concept of Objective C, I had just finished my first C++ class, so obviously, I was coming from a very bad point of view. I spent a lot of time searching for "exactly how Objective C is accomplished" and discovered it was simply a way of doing things vice having prebuilt methods (such as C++ provides) already in place.

This column will examine how C can be written not necessarily objectively but in modules and used for those who like myself are a bit unsure of what it means. It is important to note here that the ideas discussed within this context can be practiced in every language out there, C just makes for an easy example.

You Already Use It

Most libraries are written with modularity in mind, this is to make them easier to use and hence chances are you have already used them, or rather someone else's. As an example, let us look at classic error handling with my favorite, files. Here we are cracking open a file and if it cannot be opened up in the mode we want we decide to call upon some libs to help us out:

#include <stdio.h>
#include <errno.h>
#include <string.h>

#define LOCATION "/somedir/somefile"

. . .

void some_file_io()
{
	FILE *fp;	

	. . .

	if((fp = fopen(LOCATION, "r")) == NULL) {
		fprintf(stderr, ("%s: %s\n"), LOCATION, strerror(errno));
	} /* end if fopen */

	. . .

} /* end file_open_function */

. . .

You will notice we called some functions from the libraries. Within these libraries, many of these functions call other functions, many times from other libraries. This is the essence of Modular programming. There is no real need (with the exception of the curious) for us to know or even access any functions that support the one we called. All we need to know is what to expect in return (no double pun intended). To ascertain what will be returned we must consult the associated documentation (in this case ( the GNU C Library ).

When to Use It

The norm for breaking a program up into functions (not necessarily completely different files of modules) is if the main performs over four different operations of varying sorts. For example, if a main had several separate iterations, decisions and loops (not necessarily nested loops which are often used for parsing arguments) it would make more sense to simply pass along information to functions someplace else.

The same principal applies to Objective C but on a much larger scale. Let's say that we have a program that must do three major things, first it makes several system calls or uses system subroutines to access a lot of information. Then it takes this information and stores it in data structures (residing in memory) to called upon later. Next, we need to get some different information and store it, act upon that information, perhaps printing it to standard output or a file and finally run back and free up memory, clean up any temporary files and so on. In this case, we might want to attack the problem Objectively.

So why would we want to attack our aforementioned problem Objectively? Aside from readability, we have the opportunity to create Modules for ourselves or quite possibly to share with others. For instance, take the clean up routines, perhaps they can be written in a reusable manner, so if in the future if we were to write a program that had to do something similar, we could simply use those files again or copy them and make minor changes to suit our needs. It would be much simpler to find say 2 files that do a specific thing rather than searching for all of the pieces in the original program files.

We Have An Example - Let's Roll With It

So how might we approach this particular problem without going into mind numbing detail? First, we must further define the requirements (mainly for the purposes of this text). Following is a simple list in the order we want to do things:

  1. retrieve some data from the system via calls
  2. retrieve some data from the system via subroutines
  3. store data into temporary data structures
  4. organize the data structures
  5. print the data from the structures to the terminal (stdout) or a file
  6. clean up, free memory etc.

While this looks relatively simple, it is indeed fairly complex. The way we might initially approach this problem would be to rapidly prototype, or build a portion of the code, get it working and move onto the next. There is an inherent advantage to doing this because we can easily break up the problem into our separate parts and build them as we go along.

The first thing we might want to do is create some generic functions for the system calls and subroutines. Even though these are already modules we could make our own to do some additional work on them. For example, if we got a list of information from the system, within this module we could sort it, merge them or something of that nature. Our module then might have two files, one header file with the publicly accessible function prototypes we need. Then the implementations file with the public functions, private function prototypes and the private functions. If we need temporary variables or data structures, we would have consider whether or not the world would need to know about them, if not, we would put these in the implementations file, if so, we would declare them in the header file. This type of construction would be used for each subsequent module.

Next we could attempt to create a generic data structure and a set of functions to manipulate it internally. This may not be feasible due to the nature of the information. If we cannot accomplish this, then we could easily incorporate it into the program's files. We still could make it a complete module in case we needed it later on for a similar program. Again, our public prototypes would be declared in the header file along with data structures. In this case we want to organize the data, so we might want to write a function within the module that sorts it for us a certain way.

Once the Data Structure Module is finished we want to retrieve the data and either print it to the terminal (stdout) or store it in a file. Note that we do not wish to store the data structure in a file, just the information. Here we could use a module that retrieves the information, then depending on user input acts upon it. Yet again, if it is something that is severely specialized, a separate module may not help.

Finally we clean up, in this instance we could create some very simple functions that receive the objects we are deleting, freeing etc. and simply takes care of it. This may not be necessary, if you are only freeing up pointers (and not say deleting temp files, running through lists to empty out etc.) the the free function is more than adequate by itself. If we are cleaning up a lot, it might be worth our time to put in some functions that do this and let us know if there was a problem.

Writing Code to Simplify Other Code

Another aspect of Objective, Modular and Block Programming is writing code to massively simplify other code. A classic example of this is our error print out from above:

#include <stdio.h>
#include <errno.h>
#include <string.h>

#define LOCATION "/somedir/somefile"

. . .

void some_file_io()
{
	FILE *fp;	

	. . .

	if((fp = fopen(LOCATION, "r")) == NULL) {
		fprintf(stderr, ("%s: %s\n"), LOCATION, strerror(errno));
	} /* end if fopen */

	. . .

} /* end file_open_function */

. . .

That is a lot of typing, we could simply write code that simplifies the stderr messages by writing a tiny function that does this for us:

/*****************************************************************************
 * error_output_mesg                                           
 * Simple strerror print function depends upon file handle    
 * parameter to print out a file not found message			 
 *****************************************************************************/
void error_output_mesg(char *locale)
{
	/* 
	 * This is being reused simply to save typing
	 */
	fprintf(stderr, ("%s: %s\n"), locale, strerror(errno));
    exit(1);
} /* end error_output_mesg */

Next, the new revised function would check to see if we can open the file and call our new easy to remember name:

	. . .
	if((fp = fopen(LOCATION, "r")) == NULL) {
		error_output_mesg(LOCATION);
	} /* end if fopen */
	. . .

If you were doing file io on any more than four or so files, this would come in handy.

Modular programming is a life saver, you can save an immense amount of time if you do it right.


(C) Copyright 1999, 2000 OutRider
See OutRider Policies Document for more Information.
Modified on $Date: 2000/05/12 00:09:31 $ by Jason Fink ( jrf@diverge.org )