Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

C Packages


June 1992/C Packages

C Packages

Charles Measday


Charles Measday is a programmer/analyst at Integral Systems, Inc. (Lanham, MD), which builds satellite ground systems for NASA, NOAA, and the Air Force. He has been a professional programmer for about 10 years, developing satellite image processing systems in VAX/VMS FORTRAN, automated test equipment for satellite components in PL/M-286, and, currently, satellite control center software in C under UNIX. He can be reached at 1100 West Street, Laurel MD 20707. (301) 497-2563.

I recently was involved in a large software project where, as part of their quality assurance program, the main contractor established a set of standards for file and function names. The standards boiled down to two basic rules: (i) each function must have a three-character prefix that identifies the subsystem of which it is a part, and (ii) the name of a source file should match the name of the function within. The standards document, while acknowledging the common C practice of grouping related functions in a single file, encouraged programmers to store only one function per file. There are some very good reasons, however, for placing more than a single C function in one source file.

Creating Packages

Encapsulation and data hiding are important techniques for decreasing the coupling between a program's modules. The weaker the coupling between two modules, the less changes to one will affect the other. These techniques are exemplified in Ada, which hides the implementation of a capability in a package, thus shielding its clients from changes made to the implementation. An Ada package consists of two parts: a package specification, whose declarations constitute the public interface to the package, and a package body, which hides the package's actual implementation.

A C source file is analogous to an Ada package body. Static, non-local variables (i.e., those not declared in the scope of a function) in a C source file resemble variables declared in the body of an Ada package. Client modules have no knowledge of such variables and no access to them, except through declared procedures. Static functions in a C source file are like Ada procedures that are defined in the package body, but not in the package specification. Client modules cannot call these procedures.

Listing 1 shows an Ada package that manages a symbol table. The table is implemented as a simple list of name/value pairs. An internal variable, SYMBOL_LIST, points to the list. The representation of the list is not important. It could be a fixed-size array, a dynamically-allocated array, a linear linked list, a binary tree, a hash table, or even a skip list. SYM_ADD is a procedure that adds a symbol to the symbol table; SYM_DELETE deletes a symbol from the table. SYM_LOOKUP is a function that returns the value assigned to a symbol. All three functions call an internal function, SYM_LOCATE, which locates a symbol in the list and returns a pointer to the symbol's list node. SYM_UTIL_DEBUG is a global debug switch that a program can set so that the SYM_UTIL functions output debug information.

Listing 2 contains a comparable C "package," stored in a single source file. The symbol_list pointer and the sym_locate function are declared static, making them unknown outside the file. The remaining functions and the global debug flag are all accessible to the public.

Clients (users) of the symbol table package (in either language) cannot reference the symbol_list variable, have no knowledge of the structure of list nodes, and cannot call the internal procedure, sym_locate. These restrictions are not just a matter of a design methodology — they are enforced by the compiler. Breaking the C functions into separate files would force you to make the static variables global, the structure of list nodes visible throughout a program, and the sym_locate function callable from anywhere in the program. While you might trust people's good intentions and ignore the potential for malicious access to these hidden variables and functions, programs that access them out of necessity would be impacted by any change to the symbol table's implementation.

The large software project mentioned earlier was a satellite control center. It included an event-logging system that illustrates a particularly effective use of the C package concept. An application program can access the event-logging facility only through two routines, evt_init and evt_send, found in the file evt_util.c.

evt_init initializes the interface to the event logger. The fact that evt_init loads event-message information from three database files and establishes a network connection to the event logger is immaterial to the application program. evt_init could just as well open a disk file for the event log, and the text of the event messages could be hard-coded in a string array.

The details of how evt_send looks up the text of an event message, formats the message arguments, and writes the event packet out to the network are also of no consequence to the application program. You could completely revamp the internal implementation of the event-logging utilities without affecting any of the applications software. Though the applications would have to be relinked with the updated library, recompilation would be unnecessary.

Our data-services library took the opposite approach. This library, which manages network connections to multiple data servers, stores each of its functions in a different source file. Although the functions' calling sequences shield client applications from implementation details to a certain extent, the internal data structures are global. The lack of a function that built an I/O selection mask for the managed connections forced application programs themselves to scan the library's list of connected servers. This kludge forced the application to depend heavily on the internals of the data services library. In this case, a new function, ds_mask, was added to eliminate the need for the kludge. However, tracking down and eliminating such kludges can be a major maintenance headache.

Some Caveats

It is possible to close a package up too tightly. For example, as the project progressed, we found that certain applications needed to switch to a different event logger (on another computer) in mid-stream. Doing so required the application program to close the network connection to the current event logger and to open a connection to the new event logger. Hidden inside the events package, the file descriptor for the event logger connection is inaccessible to application programs. Adding a new function, evt_reconnect, easily solved the problem. Software changes may not, for organizational or configuration reasons, always be an available option.

Another word to the wise: don't hide what shouldn't be hidden. The UNIX hashing functions, hsearch(3), for example, manage a single hash table. While the details of the hash table are commendably hidden from the calling program, the calling program cannot use multiple tables simultaneously. The program must destroy one table before creating another. Rather than storing the hash table within the package, hcreate would be better off returning an opaque, void * pointer to each hash table it creates. This handle could then be passed to hsearch for adding and recalling entries from that particular table. Different hash tables would have different handles and could coexist peacefully.

Packages As OOP?

C packages can be viewed as a primitive form of object-oriented programming. (Ken Pugh alluded to this in his "Questions & Answers" column, CUJ, February 1991.) In the object-oriented approach, a program is composed of objects. An object consists of instance variables, which represent the state of the object, and methods, which are functions that modify or query the object's state. Objects communicate via messages. A message specifies a method that the receiving object is to execute, and arguments, if any, for the method.

In C, the static, non-local variables are instance variables and the functions are the methods. Calling a function is logically equivalent to sending a message to a method. Figure 1 illustrates this object-oriented view of the symbol-table package. You could consider the global debug switch, not shown in the figure, as a class variable (i.e., a variable common to all instances of a particular object type, or class).

Conclusion

C itself supports good software design principles. Rather than being encouraged to put separate functions in separate files, the C programmer should be encouraged to encapsulate functionality and data in C function packages.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.