Multiple File Compilation

Multiple File Projects:

Most of the time, full programs are not contained in a single file.  Many small programs are easy to write in a single file, but larger programs involve separate files containing different modules. Usually, files are separated by related content.

A class module normally consists of:

Filenames:
Header files are usually in the format filename.h, and implementation files are usually in the format filename.cpp. It is a good idea to use the same base filename for corresponding header and implementation files. Example:

 circle.h    // header file for a class called Circle 
 circle.cpp  // implementation file for Circle class 
Filenames do not have to be the same as the class name, but well-chosen filenames can help identify the contents or purpose of a file.

When classes are used in a program, the main program would usually be written in a separate file.
 

Compilation

The "compilation" of a program actually consitsts of two major stages.
  1. Compile stage
  2. Linking stage

Putting together a multiple-file project

For a simple example like our Fraction example, it may be tempting to simply use the following statement inside the main.cpp file:
  #include "frac.cpp"
and then just compile the main.cpp file with a single command. This will work in this example, because it's a linear sequence of #includes -- this essentially causes the whole thing to be put together into one file as far as the compiler is concerned. This is not a good idea in the general case. Sometimes the line-up of files is not so linear. The separate ideas of compiling and linking allow these steps to be done separately and there are some good reasons and benefits:

Rule of thumb: Only #include the header files, not the .cpp files!

Example CS account (g++) commands for compilation and linking:

g++ -c frac.cpp                // translates frac.cpp into object code, frac.o 
g++ -c main.cpp                // translates main.cpp into object code, main.o 
g++ frac.o main.o -o sample    // links object code files into an executable called "sample" 

Building basic multiple-file projects


Types of Errors:

  1. Compilation errors -- usually syntax errors, undeclared variables and functions, improper function calls.
  2. Linker errors -- usually involve undefined functions or multiply-defined functions or symbols
  3. Run-time errors -- two varieties.
    1. Fatal -- cause program to crash during execution
    2. Non-fatal (or logical) -- don't crash the program, but produce erroneous results.

Compilation and linker errors result in a failed compilation of the program.
Run-time errors occur while the program is running (after successful compilation).
 

Debugging:

Compile stage errors:  These are errors that will be reported by the compiler, which usually provides a filename and line number indicating where it ran into trouble, for each error reported.

Tips and suggestions:

  1. Always start at the top of the list of errors. Fix the first error, then recompile and see what is left.
  2. If a list of errors is too long, compile and debug one file at a time.
  3. When searching for an error, start with the indicated line number, but also look in the vicinity (usually previous lines) for the possible error.
  4. Compile portions of programs as you go -- don't wait until the program is fully written to do the first compile!


Linking stage errors: These errors, also reported by the compiler, do not usually contain line numbers, since the linker works on object code, not on the original source code.

Tips:

  1. Learn what kinds of problems cause linker errors (usually problems with agreement between definitions and calls).
  2. Linker errors usually specify some kind of symbol (used by the compiler), which often resembles a function or variable name. This is usually a good clue.
  3. Learn about good compilation techniques and pre-processor directives that help avoid linker errors.

Try fixing the compile and linker errors on this example program.

Run-time errors:  These must be tested while running a fully-compiled program.

Tips:

  1. To catch fatal errors, try to wedge the mistake between extra printout statements to locate the cause.
  2. To catch logic errors, place extra printout statements in code while testing (to be removed in the finished version). Especially, print out values of internal variables to locate computation problems.