NOTE:

These are a little sketchy...I'll fill in details 'soon'.

Interface File

The interface file (also known as a header file) tells other programs or libraries what facilities are available from this library. It is like a menu that others can choose from to know what constants, type definitions, and functions this library is making available to them.

Typically only constants, typedef's, and function prototypes would be found in an interface file. However, several features of C++ require us to place some code in the header file as well. In particular, inline functions cannot be prototyped and so must be defined in the interface file.

Circular Inclusion

Due to the fact that complex libraries will often include other libraries and it is possible for such a structure as:

                   +->lib A ---+
                   |           |
                 lib C        \ /
                   .           `
                  / \        lib B
                   |           |
                   |           |
                   +-----------+

Here library A needs something from library B and so include's its interface file. Library B needs a function or constant from library C and so include's its header. Sadly, library C needs a feature of library A and so include's that interface file. Now the compiler is stuck in a circle -- including these header files in sequence over and over again.

To avoid this cycle, we place some other preprocessor directives in the interface file to force the compiler to remember that it has seen that header file before and make it realize that it need not include it again. These directives are fashioned like:

    #ifndef _____SYMBOL_UNIQUE_TO_THIS_LIBRARY______
    #define _____SYMBOL_UNIQUE_TO_THIS_LIBRARY______

    // constants, typedef's, and function prototypes

    #endif

For the compiler to remember that it has seen the library before and to therefore not include it a second time, we have it ask: "Have I seen this symbol which is unique to the library yet or not?" If it has, it will skip to the #endif and be done. If it has not (the symbol is Not DEFined), it will define that symbol (remember it) and proceed with the remainder of the library's interface.

An easy and typical pattern to follow for choosing this unique symbol is to use the library's name with some extra verbage:

Library Contains Library Name Library Symbol
math functions my_math MY_MATH_H_INC
string functions strextra STREXTRA_H_INC
generally useful functions genfunc GENFUNC_H_INC
utility functions util UTIL_H_INC

Where's the Code?

So, you ask yourself, where's all the C++ code for these library functions? Well, that is in the implementation file for the library. This is a separate file where we hide all the code (and possibly other features we don't want others to see).

The interface file's extension is .h (taken from its alternative name "header"). The implementation file (being actual C++ code) ends in a .C extension.

So a library actually consists of two files: an interface and an implementation. One is the menu that tells what is available. The other is the kitchen where the meals are prepared.

The trickiest thing to remember about the implementation file is to include its own interface file before you start defining all those publically declared functions (the ones prototyped in the interface file). The reason is that: you need to be sure you have the definitions for any types and constants that were shown in the interface file. You may also have functions in this library which call each other. Without having all their prototypes included first, you'd have to make sure you defined them in the proper order in the implementation file.

Putting it All Together

When you want to use your newly created library in a program, how do you do it? Well, you'll have to first include the interface file. This is a little different for your own library than for a built-in library. Instead of the angle brackets (<>), you use double quotes ("") to surround your header file name:

   #include <iostream>

   #include "strextra.h"

   using namespace std;

Once that is done, the program will compile just fine. However, the second step of compilation (creating the executable) won't fare so well. Since the compiler doesn't have definitions for functions prototyped in the interface file, it cannot create a functioning executable (.out) for the program. The novice might think to include the implementation file:

   #include <iostream>

   #include "strextra.h"
   #include "strextra.C"

   using namespece std;

That's an evil and terribly wrong solution! Never include a C++ source file directly!

To fix this problem, what we want to do is tell the compiler where those definitions are at. We do this at compile time. If your main program were called myprog.C, you'd normally compile like this:

   CPP myprog

If this program used the strextra library discussed above, you would now have to compile it like this:

   CPP myprog strextra

This tells the compiler to compile both .C files and then combine them into a single executable.

Testing Your Library

To test a library, you need to write a driver program. A driver must call each function from the library (use each facility -- don't forget those constants and typedef's) at least once. A common pattern is to read the function's inputs from the 'user' (a programmer testing the library in this case), pass those values in, retrieve the output(s), print the output(s) for the 'user' to verify, looping as desired:

   do
   {
        // read test inputs
        // call function
        // print result(s)
        cout << "Test again?  ";
        cin >> ans;
   } while (toupper(ans) == 'Y');

And do this for each function that needs testing.

Of course, testing an entire library when you've just added a single function to the end can sometimes be tedious. You might use a primed testing loop to fix this:

   cout << "Test function ___?  ";
   cin >> ans;
   while (toupper(ans) == 'Y')
   {
        // read test inputs
        // call function
        // print result(s)
        cout << "Test again?  ";
        cin >> ans;
   }

Note how the initialization question and the update question aren't the same, but they have the same 'sense' -- that is, both continue the loop on a positive response.

The truly lazy would even work out a menu for their driver so they wouldn't have to answer 'n' to all the other functions before getting to test their new one. They could just choose it directly from the menu and test it and then quit.

Stray Bits