Topics to Cover

Breaking programs into pieces

Consider a program that reads in a vector, consisting a label and two points, and prints it back out again. The program should break up into the following obvious pieces:
point.h -- Prototypes for point stuff point.cpp -- Definitions for point-related functions

// point.h: prototypes and struct definitions
#include <iostream>
using namespace std;

struct point
{
  double x, y;
};
istream& operator >> (istream& is, point& p);
ostream& operator << (ostream& os, point p);
point operator + (point a, point b);
point operator / (point p, double z); 

// point.cpp: function definitions
#include <iostream>
using namespace std;
#include "point.h"   // NOTE 2

istream& operator >> (istream& is, point& p)
{
  char c;
  return is >> c >> p.x >> c >> p.y >> c;
}

ostream& operator << (ostream& os, point p)
{
  return os << '(' << p.x << ',' << p.y << ')';
}

point operator + (point a, point b)
{
  point S = {a.x + b.x, a.y + b.y};
  return S;
}

point operator / (point P, double z)
{
  point Q = {P.x / z, P.y / z};
  return Q;
}

main.cpp -- New code with definition for Vector and main

#include <iostream>
using namespace std;
#include "point.h"    // NOTE 1

struct Vector
{
  char label;
  point s, t;
};

int main()
{
  // Create a vector V
  Vector V;

  // Read label and vertices
  cout << "Enter label and 2 vertices: ";
  cin >> V.label >> V.s >> V.t; 

  // Write label and vertices
  cout << "Your vector is " << V.label << " "
       << V.s << " " << V.t << endl;

  return 0;
}

NOTE 1: #include "point.h" in main.cpp

Now, to use the struct point and all of its related functions, we really only need the function prototypes and struct definition - the definitions of the functions don't really concern us as users of point. We only care about what's available for us to use, not how it works. So, the file has the line:
#include "point.h"
which means literally pretend that the entire contents of point.h was typed in starting at this point. Thus, as far as the compiler is concerned, it's like that struct definition and all those prototypes were right there.

NOTE 2: #include "point.h" in point.cpp

You'll notice that point.cpp also begins with #include "point.h", since those function definitions won't make sense to the compiler without the definition of the struct point and the prototypes.

Compiling the source code files: give all cpp (but no h) files

You can simply compile the source files as follows:
g++ main.cpp point.cpp -o vector
Note: No .h files should be in the g++ compling command.

Benefit of creating a library

Breaking programs up into separate files this way doesn't really change what you can or can't do, but:

Library

In fact, your start thinking of big programs as different libraries or modules to write: What you saw up above was a very simple point library.

One key thing that's missing from my simple point library, however, is documentation. The header file needs a lot of documentation so that the user of the module understands how to use the struct and the various functions the module offers.

The most important note you'll ever read: Programming nirvana - the most enlightened state of programming - is reached when you have perfectly achieved two goals:
  1. separation of interface from implementation, i.e. what is needed in order to use a thing is completely walled off from how the thing actually works, and
  2. code reuse, i.e. code you write for one project can be easily reused in all sorts of other projects, or reused within the same project (never duplicate code!).

Guarding header (.h) files

Rule for struct definition

When source code files are compiled in C++, there is a rule about how often struct definitions can appear.

Problem

Now, there's one problem that can come up, which is illustrated by the following example. Let's split main.cpp into two parts: Vector and main().

vec.h main2.cpp

#include "point.h"
struct Vector
{
  char label;
  point s, t;
};

#include <iostream>
using namespace std;
#include "point.h"     // NOTE HERE
#include "vec.h"      // NOTE HERE

int main()
{
  // Create a vector V
  Vector V;

  // Read label and vertices
  cout << "Enter label and 2 vertices: ";
  cin >> V.label >> V.s >> V.t; 

  // Write label and vertices
  cout << "Your vector is " << V.label << " "
       << V.s << " " << V.t << endl;

  return 0;
}
Now look what happens when we try to compile:
$ g++ point.cpp main2.cpp -o vector
In file included from vec.h:1,
                 from main2.cpp:4:
point.h:7:8: error: redefinition of ‘struct point’
    7 | struct point
      |        ^~~~~
In file included from main2.cpp:3:
point.h:7:8: note: previous definition of ‘struct point’
    7 | struct point
      |        ^~~~~
The problem is that this causes the struct point to be defined twice in main2.cpp. Why?
  1. So, main2.cpp #includes point.h. This gives the first definition of point in main2.cpp
  2. However, main2.cpp also #includes vec.h.

#pragma once

The way around this problem is to guard your .h files with the following:
#pragma once
It is widely supported preprocessor directive designed to cause the current source file to be included only once in a single compilation. The preprocessor is invoked by the compiler as the first part of translation. One of the jobs of the preprocessor is taking care of inclusion of header files.

Remember

point.h vec.h

#pragma once     // **** NOTE HERE
#include <iostream>
using namespace std;

/*********************************************
* * PROTOTYPES & STRUCT DEFINITIONS
* ********************************************/
struct point
{
  double x, y;
};
istream& operator >> (istream& is, point& p);
ostream& operator << (ostream& os, point p);
point operator + (point a, point b);
point operator / (point p, double z); 

#pragma once
#include "point.h"
struct Vector
{
  char label;
  point s, t;
};
Now, the files compile well.
$ g++ point.cpp main2.cpp -o vector
$

Makefile

Sometimes it gets pretty annoying to type the compilation commmand, or remember how to compile each of your two executables.

To make this easier on ourselves, we're going to make a file called Makefile (notice how there's no file extension).
Makefile
vector: main2.cpp point.cpp point.h vec.h
         g++ point.cpp main2.cpp -o vector 

Running a command vector in a terminal

With this Makefile in my directory, I need only run the following in a terminal.
$ make vector
Then, it will find that command vector in the Makefile and run the corresponding g++ command.

Dependency list

The files in the dependency list are those that the commmand vector is sensitive to.

Adding a tab

When you write a Makefile, please be careful and give an actual tab before a g++ command. It is not a bunch of spaces; it's a single tab.