Department of Engineering

Using multiple source files

In the IDP you'll be writing several hundred lines of C++ code. This document will try to explain why it's not a good idea to put all the code in one file, and explain how to organise your files.

Background information - compiling

Compilation is a multi-stage process (an example from another project is on the right). The first stage is pre-compilation, when the lines beginning with # are processed. When the pre-compiler reads

#include "header.h"

in a file called main.cc, it will read in the contents of header.h from the current folder (like on-the-fly copy-pasting). As far as the next stage of compilation's concerned, it's as if contents of "header.h" were in "main.cc". The compilation stage converts the source code into low-level fragments. These are joined up along with other libraries of code to produce a machine-code file that the CPU can run.

Avoiding making the compiler work too hard

If you have all the code in one file, all the file needs to be recompiled even you change just one line. It's tidier to have several files of code. The "Make" facility will ensure that the only files recompiled will be the ones that have been changed since the last recompilation.

Another advantage of having many files is that programmers can simultaneously work on different files of a project.

So, for example you might decide to have main.cc, navigation.cc, etc. We've set the IDP geany program up so that if you drop the files at the same time into the geany icon, they'll be assumed to belong to the same project.

Calling functions

Suppose you have created a function in navigation.cc called turn_left() and you tried to call it from the main.cc file. Unless you're careful the compiler won't like this because it wants to check that you've called the function correctly (with the correct number of input parameters, etc) but you've not told it that information. That information can be supplied to the compiler when it reads main.cc by supplying a function prototype. A function prototype is like the first line of a function with a semicolon at the end, so putting

int turn_left();

at the top of the main.cc file would let you use the turn_left() function from main.cc.

Avoiding code duplication

Repeating the prototype at the top of each file is error-prone. If instead you put the prototypes for all the functions in header.h and include it in each file, you won't need to have lots of copies of the prototypes. Note that there's no need to have the code of the functions in header.h, just the prototypes suffice.

Global variables

It's possible that all these files might need to access the same variable. For example, the robot_link rlink variable might be used in all the files. Putting robot_link rlink; at the top of each file won't work, because you'll have a separate variable in each file. Putting robot_link rlink; in header.h has the same effect.

The solution is to

  • create the variable in one (and only one) file outside of all functions.
  • in the other files put extern robot_link rlink;. This is telling the compiler that external to the current file, there's already a variable called rlink

In fact, you can put extern robot_link rlink; in header.h even if the file where you create rlink includes header.h - the compiler will understand what you want.

Summary

  • Use files to keep related code together (and unrelated code apart)
  • Use header files for prototypes, externs, and constants, not for code.
  • Don't use #include to include source files that have code in them. If your project's code was in main.cc, navigation.cc and motor.cc you could, in main.cc, have
    #include "navigation.cc"
    #include "motor.cc"
    
    and drop the main.cc file into the geany icon. It might well compile. The trouble is that the pre-compiler would read in these 2 files, so that the compiler would essentially process one big file, thus losing some of the benefits of having multiple files.