Data Structure and Algorithm Design
Chapter 2
Xingang (Ian) Fang
Sections
Modular Development in C++
Testing for C++
Modular Development in C++
Xingang (Ian) Fang
Outline
Overview
Definition
Motivation and benefits
Source code organization
C++ building model
Building using GNU g++
Preprocessing directives
One step compilation
Step-wise modular compilation
Automate the building process using GNU make
Makefile
Modular compilation using make
Pitfalls and common errors
TL;DR
Must go modular in this course!
No longer have single-cpp-file projects.
Organize source code as header and source files.
Logically organized as drivers and modules.
Use g++ step-wise compilation
Learn basic makefile syntax
Modular Development Overview
Definition
Modular development in C++ refers to the practice of designing and structuring a software application as a collection of independent and reusable modules or components. Each module encapsulates a specific set of functionality, and these modules can be developed, tested, and maintained separately from each other.
Motivation and benefits
Code reusability
Simplicity
Encapsulation
Parallel development
Flexibility
Testability
Source Code Organization
Two types of files (physical units)
Header files
contain declarations of public classes, functions, global variables, etc.
have extension .hpp
Source files
contain definitions of public classes, functions, etc.
contain private declarations and definitions
preferred extension .cpp
Logical units
Drivers
contains a ``main`` function
generally have no paired header file
one per executable
E.g.
main.cpp
,test.cpp
,driver.cpp
, etc.
Modules (or libraries)
no ``main`` function
contain classes, functions, global variables, etc. to be used by the driver or other modules
paired hpp and cpp files or header only
E.g.
table.hpp + table.cpp
,list.hpp + list.cpp
, etc.this term is used interchangeably with “library” in this course; it may be defined differently in other contexts
Quick Quiz
In a project with files: main.cpp
, table.hpp
, table.cpp
, and
list.hpp
, find which files belongs to the following categories:
Driver
Module (paired files)
Module (header only)
C++ Building Model
The process of translating source codes into executables or libraries is called building.
Inherited from C, C++ building process is complicated and tedious.
The building process consists of three stages:
Preprocessing
process all preprocessor directives
the
#include
directive injects hpp file contents into cpp files
Compiling
compile cpp files into object files
one object file for each cpp file
Linking
link object files into an executable
link object files with libraries (FYI)

Building using GNU g++
Overview
g++ is the GNU C++ compiler invocation command. It builds C++ source codes to produce libraries or executables. It serves as pre-processor, compiler, and linker for C++ projects.
g++ command syntax
g++ [options] [files]
files
: source files, object filesoptions
: a list of options to control the building process
Preprocessing directives
Commands to the preprocessor.
They start with
#
and end with no semicolon.#include <header>
: inject the content of header file into the current file.#define
: define a macro (text replacement rule).Header guards: prevent header files from being included multiple times.
#ifndef HEADER_GUARD #define HEADER_GUARD // header file content #endif
Two building approaches: One step
Not recommended for modular projects.
Should be limited to simple projects.
Preprocessing + Compilation + Linking all at once
Generate one executable from a single driver and other source files for modules
g++ -std=c++14 -I<path/to/headers> -o <executable> <list of .cpp files>
Two building approaches: Step-wise
Recommended for modular projects.
Allows fine control over the building process of complicated modular projects.
Preprocessing + Compilation
g++ flag:
-c
generate one object file for each cpp file
g++ -c -Wall -std=c++14 -I<path/to/headers> <file>.cpp
Linking
generate one executable from object file of a driver and other object files of modules
g++ -o <executable> <list of .o files>
Common errors: link
Never Do This!
Include cpp files using #include directive
File name mismatch between #include directive and actual file name; Commonly caused by the use of capital letters in file names
Include header file names in the g++ command line
Linking object files of multiple drivers into one executable
Forget to include header guards in header files
Forget
-c
flag in the compilation command
Quick Quiz
What is wrong with the following commands? What type of building approach is?
g++ -o main main.cpp grade-book.cpp grades.hpp
g++ -o main.o main.cpp
g++ -c -o main.o main.cpp grade-book.cpp
Automate the building process using GNU make
Overview
Step-wise modular building is tedious and error-prone.
GNU make is a tool to automate building process.
make
is the command-line command to trigger GNU make.
Makefile
The configuration file that contains rules to guide the
make
command to build targets in a project.Modular building using GNU make
Define rules to compile and link
Have complicated syntax for makefile but easy to start with
Common errors: link
# declare virtual targets
.PHONY: clean
# Linking to provide the executable
main: main.o grade-book.o student.o grades.o
g++ -o main main.o grade-book.o student.o grades.o
# Compile. One cpp file a time.
main.o: main.cpp grade-book.hpp student.hpp
g++ -std=c++14 -Wall -g -c main.cpp
student.o: student.cpp student.hpp
g++ -std=c++14 -Wall -g -c student.cpp
grades.o: grades.cpp grades.hpp
g++ -std=c++14 -Wall -g -c grades.cpp
grade-book.o: grade-book.cpp grade-book.hpp student.hpp grades.hpp
g++ -std=c++14 -Wall -g -c grade-book.cpp
# the 'clean' virtual target to remove temporary files
clean:
rm -rf *.o main *.gc* *.dSYM
Use GNU make to build
Create a makefile in the project directory.
Define rules for compiling and linking.
Run
make
commands:make
: build the first target in the makefile, you can press tab to auto-complete the target namemake <target>
: build the specified targetmake clean
: remove temporary files
Common make targets in projects
make test-all
: run all testsmake test-#-name
: run test number #make test-run
: compile and run the program
Testing For C++
Xingang (Ian) Fang
Outline
Definition
Motivation
Types
Learning outcomes
TL;DR
“Debugging” is too hard! Use “testing” instead.
Write drivers (cpp file with main function) to test your code.
Feed input and check output.
Writing more code (testing code) will actually save you time.
Expect you to learn to read tests first in this course.
Overview
Definition: Testing in software development is the process of evaluating and validating a software system to ensure its correctness, functionality, and performance. Automated testing is especially important for large projects.
Motivation and benefits
Large projects are hard to test manually.
Automated testing is more efficient and reliable.
Tests can describe the functionality of the system (requirements).
Testable code is usually modular and well-organized.
Types
Unit tests
Integration tests
System tests
Test-driven development (FYI)
Write tests first
Write code to pass tests
Types of tests
Unit tests
Test a single function or class method.
Test the correctness of the function/method.
Test the boundary cases.
Test the performance.
May need special techniques to isolate the function/method.
Integration tests
Test the interaction between modules.
Test the correctness of the interaction.
System tests
Test the system as a whole.
Test the correctness of the system.
Test the performance.
Write and run tests
Workflow
Write test drivers that call functions/class methods to be tested.
Add building rules for test drivers in the makefile.
Frequently run tests to ensure the correctness of the code.
Simple test driver
Display the result of a function call for the tester to compare.
#include <iostream> #include "store-item.hpp" void testStoreItem() { StoreItem item1("Apple"); StoreItem item2("apple"); if (item1.equals(item2)) std::cout << "They are equal as expect!\n"; else std::cout << "They are not equal, wrong!\n"; }
Write and run tests (cont’d)
Test driver using assert
assert
is a macro that checks the truth of an expression.If the expression is false,
assert
prints an error message and aborts the program.assert
is a simple way to write test drivers.include
<cassert>
to useassert
.
#include <cassert> #include "store-item.hpp" void testStoreItem() { StoreItem item1("Apple"); StoreItem item2("apple"); assert(item1.equals(item2)); }
Write and run tests (cont’d)
Test driver using testing frameworks
Testing frameworks are libraries that provide tools to write tests.
They are more powerful than
assert
.E.g. Google Test, Boost Test, Catch2 etc.
We use Catch2 in this course.
#include "catch.hpp" #include "store-item.hpp" TEST_CASE("StoreItem equality and printing", "[StoreItem]") { SECTION("Case-insensitive comparison") { StoreItem item1("Apple"); StoreItem item2("apple"); CHECK(item1.equals(item2)); } }
Learning outcomes
Understand the importance of testing.
Know how to write simple test drivers.
Know how to write test drivers using
assert
macro.Know how to read test cases written with Catch2 framework and write code to pass the tests.