Lecture 7

Last Time: Const Iterators, Templates, Separate compilation This Time: Preprocessor, Makefiles, Tooling What is #ifndef pattern?

The files we gave in A1 looked like:

#ifndef "MOTION2D_H"
#define MOTION2D_H
...
#endif

Preprocessor directives - commands that allow transformations of the source code before it is compiled. #include file - replaces this line with the contents of file.

Consider an example: Linear Algebra Modules

// Vec.h
struct Vec {
	int x, y;
};
// Matrix.h
#include 'vec.h'
struct Matrix{
	Vec e1;
	Vec e2;
};
// main.cc
#include "Vec.h"
#include "Matrix.h"
int main(){
	Vec v1{1, 2};
	Vec v2{3, 4};
	Matrix m{v1, v2};
 
}

We get a compilation error: main.cc includes Vec.h main.cc includes matrix.h, includes Vec.h Two definitions of struct Vec in main.cc not allowed.

To fix this issue of multiple definitions, we use “include guards”:

#ifndef
#define
 
#endif

#ifndef VARIABLE The code between #ifndef and #endif will only be seen by the compiler if VARIABLE is not defined. #define VARIABLE defines a preprocessor variable.

Include Guard:

#ifndef FILE_H
#define FILE_H
...
#endif

Works because once File.h is included once, the variable becomes defined, so in all future includes the block is omitted.

Note

Doesn’t fix all issues! There is an issue with circular dependencies.

// A.h
#include "B.h"
class A{
	B* myB;
};
// B.h
#include A.h
class B{
	A* myA;
};

The issue is that each class needs to know the other exists before it can be created.

Solution: Break circular dependency chain using forward declarations.


Separate Compilation Speeds up compilation and development time: change one .cc file update the .o file,

Issue: if we change a .h file, then many .c files might need to be recompiled. Mental energy to figure out the dependencies and just recompile the relevant .cc files. Might be faster to just recompile everything.

Solution: Use a build automation system. Keep track of what files have changed, keep track of dependencies in compilation, and just recompiles the minimal number of files to make a new executable.

We’ll discuss make: Create a Makefile.

Example: main.cc includes List.h, List.h, List.cc includes List.h

Make v1: Need to identify the target and dependencies.

myprogram: main.o List.o

  • target: myprogram
  • dependencies: main.o List.o

Target(TAB) is myprogram g++ main.o List.o -o myprogram

main.o: main.cc List.h g++ -std=c++14 main.cc -c

List.o: List.cc List.h `g++ -std=c++14 List.cc -c

This text is in a Makefile in our directory. make creates the first target Looks at the last modified time of dependencies. If last modified time is newer for a dependency than a target target is out of date, recreate it.

Still too much work for me! Still requires lots of updates to our makefiles.

Make v2:

  • CXX=g++ - is a special Makefile variable for compiler we’re using.
  • CXXFLAGS= -std=c++14 -Wall -g -MMD
    • -Wall - more warnings
    • -g debugging
    • CXX=g++ is a special Makefile variable
    • -MMD supports
  • EXEC=myprogram
  • CCFILES=$(wildcard *.cc)
  • OBJECTS=${CCFILES:.cc=.o}
    • find and replace string operation {}
  • DEPEND=${CCFILES: .cc=.d}
CXX=g++
CXXFLAGS= -std=c++14 -Wall -g -MMD
EXEC=myprogram
OBJECTS=${CCFILES:.cc=.o}
DEPEND=${CCFILES: .cc=.d}
${EXEC}: ${OBJECTS} 
	${CXX} ${OBJECTS} -o ${EXEC}
include ${DEPENDS}
  • include ${DEPENDS}
    • compiles all object files with dependencies using CXX and CXXFLAGS

We’ve discussed a lot of programming ingl but we haven’t discussed methodology, other tooling yet.

Software development lifecycle:

Waterfall Method: client specs program build Test/debug it use source control release it