Previous Lecture Lecture 6 Next Lecture

Lecture 6, Thu 07/19

C++ Build Process, Makefiles, Test-Driven Development

C++ Build Process

Header Files

Example

------------------------------------
// drawShapes.h
#include <string>
using namespace std;

string drawRightTriangle(int height);
string drawSquare(int length);
------------------------------------
// drawShapes.cpp
#include <string>
using namespace std;

string drawRightTriangle(int height) {
	string result = "";
	int rowLength = 1;

	for (int i = 0; i < height; i++) {
		for (int j = 0; j < rowLength; j++) {
			result += "*";
		}
		result += "\n";
		rowLength++;
	}
	return result;
}

string drawSquare(int length) {
	string result = "";

	for (int i = 0; i < length; i++) {
		for (int j = 0; j < length; j++) {
			result += "*";
		}
		result += "\n";
	}
	return result;
}
------------------------------------
//program1.cpp
#include <iostream>
#include "drawShapes.h"

int main() {
	cout << drawRightTriangle(5) << endl;
	cout << drawSquare(5) << endl;	
return 0;
}
------------------------------------

What’s with the different type of #include<> or #include “”?

Similar to how we compile C++ programs with a single file, we can compile all source (.cpp) files using:

g++ -o program1 program1.cpp drawShapes.cpp

g++ -c -o drawShapes.o drawShapes.cpp
g++ -c -o program1.o program1.cpp
g++ -o program1 program1.o drawShapes.o

C++ Build Process

  1. Preprocessing: Text-based program that runs before the compilation step. Looks for statements such as #include and modifies the source which is the input for compilation.
    • Think of the compiler “copying / pasting” the contents of the included file everytime #include is used.
  2. Compilation: Translates source code into “object code,” which is a lower-level representation optimized for executing instructions on the specific platform. Lower level representations are usually stored in a .o (object) file.

  3. Linking: Resolves dependencies and maps appropriate functions located in various object files. The output of the linker is an executable file for the specific platform.

But what happens if we have hundreds of source files to compile?

Makefiles

General Format of a Makefile

[target]: [dependencies]
	[commands]

Example

------
# Makefile

# Single line comments in Makefiles use '#'

program1: program1.o drawShapes.o
	g++ -o program1 program1.o drawShapes.o

clean:
	/bin/rm -f *.o program1
------

Example

$ make program1
c++    -c -o program1.o program1.cpp
c++    -c -o drawShapes.o drawShapes.cpp
g++ -o program1 program1.o drawShapes.o
$ make program1
c++    -c -o program1.o program1.cpp
g++ -o program1 program1.o drawShapes.o
$ make program1
make: `program1' is up to date.

Another Example of a Test Program

Test-Driven Development

------------------------------------
// tdd.h
#include <string>
using namespace std;

void assertEqual(string expected, string actual, string message="");
------------------------------------
// tdd.cpp
#include <iostream>
#include <string>
using namespace std;

void assertEqual(string expected, string actual, string message = "") {
	if (expected == actual) {
		cout << "PASSED: " << message << endl;
	} else {
		cout << "\tFAILED: " << message << endl;
		cout << "Expected: " << "[\n" << expected <<
		"\n]" << "Actual: [\n" << actual << "\n]" << endl;
	}
}
------------------------------------
//testDrawShapes.cpp
#include <iostream>
#include <string>
#include "drawShapes.h"
#include "tdd.h"
using namespace std;

void testDrawRightTriangle() {
	string expected1 =
	"*\n"
	"**\n";

	string actual1 = drawRightTriangle(2);
	assertEqual(expected1, actual1, " testHeight:2");

	string expected2 =
	"*\n"
	"**\n"
	"***\n";

	string actual2 = drawRightTriangle(3);
	assertEqual(expected2, actual2, " testHeight:3");
}

void testDrawSquare() {
	string expected1 =
	"**\n"
	"**\n";

	string actual1 = drawSquare(2);
	assertEqual(expected1, actual1, " testLength: 2");

	string expected2 =
	"***\n"
	"***\n"
	"***\n";

	string actual2 = drawSquare(3);
	assertEqual(expected2, actual2, " testLength: 3");
}

int main() {
	testDrawRightTriangle();
	testDrawSquare();
}
------------------------------------
# Makefile

testDrawShapes: testDrawShapes.o drawShapes.o tdd.o
	g++ testDrawShapes.o drawShapes.o tdd.o -o testDrawShapes

clean:
	/bin/rm -f *.o testDrawShapes
------------------------------------
make testDrawShapes

More information on compilation / makefiles: