Libraries

A library is just a special word for a source code file that groups, or gathers, related operations. We can think of it as a package containing snippets of code that make our main driver (the source code file containing the brunt of our program) easier to produce, test, maintain, and ship.

A C++ library consists of two parts: (1) a header file, and (2) an implementation file. The header file—denoted by the extension .h—provides all of the function prototypes (e.g., names and types) and usage comments. The implementation file, denoted by the extension .cpp—implements everything in the header file.

Libraries are a means of modularization—they break down and separate source code into smaller pieces. For example, consider the following program:

For example, consider the following program:

#include <iostream>
using namespace std;

double absolute_value(double n) {
	if (n < 0.0) {
		return -1.0 * n;
	} else if (n > 0.0) {
		return n;
	} else {
		return 0;
	}
};

double square(double n) {
	return n * n;
};

double sqrt(double n) {
	const double EPSILON = 0.001;
	double lower, upper, guess;
	if (n < 1) {
		lower = n;
		upper = 1;
	} else {
		lower = 1;
		upper = n;
	}
	while ((upper - lower) > EPSILON) {
		guess = (lower + upper) / 2;
		if (square(guess) > n) {
			upper = guess;
		} else {
			lower = guess;
		}
	}
	return (lower + upper) / 2;
};

int main() {
	double a = 4.0;
	double b = square(a);
	double c = sqrt(a);
	cout << "The number is: " << a << endl;
	cout << "It's square is: " << b << endl;
	cout << "It's square root is: " << c << endl;
	return 0;
}

In the program above, we have three functions, absolute_value(), square(), and square_root(). The main driver, indicated by the function main(), is fairly simple. It just computes and outputs the square and square root of 4.0. But just look at the size of this thing. It's huge, for a relatively simple computation. What we instead should do is "package" the functions absolute_value(), square(), and square_root() into a library.

The first step is to write all of the function prototypes inside a header file. Following best practice, that file should be the name of our library. Let's call it maths.h:

// maths.h
double absolute_value(double n);
double square(double n);
double sqrt(double n);

Notice maths.h only contains the function protoypes. This is intentional. A header file represents the interface. It should only inform the user what the function does; implementation details shouldn't be presented.

Second step: Implement the functions. All the details surrounding the interface's implementation (i.e., how the functions in the header file are implemented) are placed in a separate source code file. Again following best practice, that source code file should have the same name as the header file it implements. Thus, we'll call it maths.cpp:

// maths.cpp
#include "maths.h"

double absolute_value(double n) {
	if (n < 0.0) {
		return -1.0 * n;
	} else if (n > 0.0) {
		return n;
	} else {
		return 0;
	}
};

double square(double n) {
	return n * n;
};

double sqrt(double n) {
	const double EPSILON = 0.001;
	double lower, upper, guess;
	if (n < 1) {
		lower = n;
		upper = 1;
	} else {
		lower = 1;
		upper = n;
	}
	while ((upper - lower) > EPSILON) {
		guess = (lower + upper) / 2;
		if (square(guess) > n) {
			upper = guess;
		} else {
			lower = guess;
		}
	}
	return (lower + upper) / 2;
};

Notice the #include maths.h. This is what effectively tells C++ that maths.cpp implements math.h.

Third step: We include the header file, maths.h, inside the main driver (a file named main.cpp):

// main.cpp
#include <iostream>
#include "maths.h"
using namespace std;

int main() {
	double a = 4.0;
	double b = square(a);
	double c = sqrt(a);
	cout << "The number is: " << a << endl;
	cout << "It's square is: " << b << endl;
	cout << "It's square root is: " << c << endl;
	return 0;
}

Notice the #include "maths.h". This effectively dumps all of the contents of maths.h into the main.cpp. Our main driver is now much, much shorter and cleaner.

Now, to run this code, we'll need to compile main.cpp and maths.cpp, then link their resulting executables into a single executable.

g++ -c maths.cpp
g++ -c main.cpp
g++ -o mainProgram main.o maths.o
./mainProgram

The number is: 4
It's square is: 16
It's square root is: 2.00012

We can make the entire compilation and linking process above easier by writing a Makefile. To do so, we simply create a new file called Makefile inside our directory, and write the following code inside:

CC = g++
CFLAGS = -Wall -g
objects = main.o maths.o
all: $(objects)
clean:
	$(RM) *.o all
main: $(objects)
	$(CC) $(CFLAGS) -o mainProgram $(objects)

This leads to a much faster and easier compilation and linking process:

make
make main
./mainProgram

The number is: 4
It's square is: 16
It's square root is: 2.00012

Whenever we write #include ${f}$, where f{f} is some library, in our source code file, the underlying process is essentially what's seen above. The contents of f{f} are dumped into the source code file, where they are used and treated as if we wrote them directly into the source code file.