home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
C/C++ Users Journal 1990 - 1995
/
CUJ.iso
/
unix
/
1992.txt
< prev
next >
Wrap
Text File
|
1996-02-07
|
3MB
|
100,450 lines
Porting C Libraries To C ++
David Brumbaugh
David Brumbaugh is a project manager at Advanced Information Services, a
systems integrator in Peoria, IL. He has been programming in C for over five
years and in C++ for over a year. He can be reached by mail at 2807 N. Renwood
Ave., Peoria, IL 61604.
Most C programmers have large investments in existing C libraries. If C++ is
to fulfill the promise of reusable software, the C libraries of today cannot
suddenly become obsolete. Usually, existing libraries can be given "OOPness"
by encapsulating their functions in C++ classes. Furthermore, if those classes
are well-designed, they can enhance an application's portability, readability,
and maintainability.
C++ And C
For all practical purposes, C++ is a superset of C. C++ can be linked with
code compiled by a C compiler if the functions are prototyped and enclosed in
the linkage specifier:
extern "C" { }
This declaration lets you use C libraries directly, without modification. Of
course, you must have a compatible object file to begin with. You can link a
Turbo C 2.0 library and a Turbo/Borland C+ + program, but you cannot use the
declaration to link a Turbo/Borland C+ + program and a Microsoft C 5.1
library. (I know because I tried.) While this scheme doesn't exploit C+ +'s
bent towards OOP, almost anything your old C libraries can do can be made to
work in C++.
Terms
Cutting away the object-oriented jargon leaves little difference between a
method and a function. Objects contain both data and functions. In OO jargon,
these are attributes and methods. (For the purposes of this article, I use
"method" to denote a class's member function and "function" to mean a
traditional C function.) In many cases libraries already contain most of the
code for the methods. To make the libraries OO, you simply need to wrap
classes around the libraries.
Preparation
In many cases, it is not necessary to wrap libraries. A library that is not
often used or has no common thread probably is not a candidate for wrapping.
On the other hand, a method may need some functions found in a library that is
not wrapped itself. In this case, simply enclose the C function in extern "C"
{ } and call it as necessary. Often you can use conditional compilation in the
library's header file. (See Listing 1. The __cplusplus macro is Turbo/Borland
C++ specific.)
Before wrapping a class around a library, ask yourself what advantages you
expect. Typically, these might include:
Common Access. By overloading methods, it isn't necessary to create functions
named replaceString, replaceInt, replaceReal. A replace method will suffice.
Code becomes more readable and, in turn, more maintainable.more readable and,
in turn, more maintainable.
Reusable Code. Object-oriented code is more reusable because it encourages
encapsulation. In traditional programming, programmers tend to reuse code by
using an editor's "cut and paste" functions. While this works, the same code
in different locations frequently requires slight changes in variable names,
function names, or control structures. An object-oriented style enhances code
reusability by localizing and formalizing the areas being changed.
Localized Impact of Changes. Changes in understanding, requirements, and
design prompt changes in program code. A typical problem arises when two areas
of a program call the same function. If that function is subsequently modified
to accommodate a change in one of the two areas, the other area may behave
improperly. An object-oriented solution creates a descendant of the object in
the first area, where the change is needed. All other code remains the same.
Again, the change is local and formal.
Portability of Applications. By isolating platform-specific libraries from
your application in private and protected methods, you can change the
implementation details of a specific class without touching the actual
application code (this is a variation on number three). While preparing to
wrap the library in a class, remember that in C++, all functions need
prototypes. Most libraries come with a header file containing the prototypes.
If your library doesn't come with such a header file, you must make one in
order to use the functions in C++.
C++ has several keywords not found in C: asm, catch, class, delete, friend,
inline, new, operator, private, protected, public, template, this and
virtual[1]. If any of your library functions have these names, you will have
problems. To work around this, remove the prototype of the offending function
from the header file using conditional compilation. Write a new function, with
a different name, in C that calls the library function (see Listing 2 for an
example).
The final consideration for encapsulation concerns the availability of source
code. If you have purchased the source code for a commercial library, you may
want to avoid the overhead of wrapping, and simply use the cut and paste
method to build classes from the original source.
Designing Classes Around Libraries
Since a class encapsulates both data and functions, you must identify data
structures in the library that can become attributes of your class. As a
general rule, data structures should be made protected variables. You will
want to group common functions and data structures into a class.
Once you have identified the common data structures, determine the class
protocol. A class protocol is the list of methods used to send messages to
objects of the class. In C++, the class protocol is the set of public methods
and data items. Use generic names for the methods in your protocol. A database
library may have several read functions: db_read_int(), db_read_str(),
db_read_float(), etc. The protocol for this set of functions would be read(int
&), read(char *), read(float &), etc. The bodies of these methods would
contain calls to the proper library functions.
Avoid the temptation to give class methods library-specific names. Create
general classes that permit inheritance in the future. For example, a generic
database class could have descendants that call two completely different
database libraries. Changing from one database to another would require no
changes to the application.
Just because a function is in a library does not mean it must be represented
in the class. In the example that follows, the Pfm_List class concerns itself
with a single table in a database. It does not need database creation or
administration functions.
Round out the class with internal methods. In C++, methods designated as
private or protected are internal and are used by other methods. The library
will generally dictate the internal methods, but don't allow it to dictate the
overall class concept.
Pinnacle File Manager Tables
I have translated into C+ + an example that I wrote for an earlier CUJ
article, "Object-Oriented Programming In C" (July 1990). I used the Pinnacle
File Manager v3.5 by Vermont Database Corporation as the library to
encapsulate. (If you wish to run the code in the examples, you can obtain a
free sample disk that is limited to 100 records per table from Vermont
Database Corporation by calling 802-253-4437.)
I first needed to decide how to implement the list concept I presented in July
1990. Turbo C++ came with a Container class library, which met some of the
needs I was trying to meet with the LIST_CLASS. Should I try to fit my code
into their hierarchy or create my own hierarchy? I finally decided to use my
own. This hierarchy is shown in Figure 1.
A D_List defines the operations you would normally