Modular Development

TL;DR

  • No more single file project except in Intro to C++ class.

  • Learning how modules work together.

  • Use make to automated the building process.

  • Create source files

    • hpp/cpp pair for normal module

    • hpp for header-only module (class templates)

    • cpp for driver

Overview

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.

Unless in the introductory courses, we will always use modular development rather than single-file development. You can not survive the course if you stick to single-file development.

Advantages

  • Code reusability: Modules can be designed and implemented as stand-alone entities, which can be reused in other projects. This can help reduce development time and improve overall code quality.

  • Simplicity: By breaking down a large program into smaller modules, it becomes easier to manage and understand the code. This makes it easier to debug and maintain, as well as more readable for other programmers.

  • Encapsulation: Modules provide a way to encapsulate data and functions, allowing them to be accessed only through a specific interface. This can improve code security and help prevent errors caused by unintentional modifications of data or functions.

  • Parallel development: Modular development allows multiple programmers to work on different modules simultaneously without interfering with each other’s code. This can significantly speed up the development process and improve overall efficiency.

  • Flexibility: Modules can be developed and tested independently, making it easier to modify or add functionality to a program without affecting other parts of the code. This can help make the program more adaptable to changing requirements and user needs.

  • Testability: Modular development allows for easier testing of individual components, which can help detect errors earlier in the development process and improve overall code quality.

C++ Building Process

The process of translating source codes into executables or libraries is called building.

The building process of a modular C++ project generally involves three main steps: pre-processing, compilation, and linking. External libraries may also be involved in the process.

  • Pre-processing: In this step, the pre-processor examines the source code files and performs tasks such as including header files, expanding macros, and removing comments. The pre-processor outputs a modified version of the source code, which is then sent to the compiler.

  • Compilation: The compiler takes the pre-processed source code and translates it into machine-readable object code. Each source code file is compiled independently, resulting in one object code file for each source file.

  • Linking: In this step, the linker takes all of the object code files and combines them into a single executable program or library. During this process, unresolved external references are resolved by linking with external libraries.

If the modular C++ project uses external libraries, the libraries need to be included in the build process. This can be done by specifying the library file paths and names in the linker command line or in a configuration file.

It’s important to note that the building process for a modular C++ project can vary depending on the specific build tools and development environment used. However, the general steps of pre-processing, compilation, and linking are common to most build processes.

It is discussed in detail in the g++ document.

C++ Building Process of an Executable

Multi-file Organization in C++

Types of Files

Header file

A header file is a text file that is included by other files to share public contents such as function and class declarations, as well as public configurations such as variables, macros, and type aliases.

Source code file

A source code file is a text file that is compiled to create an object file. It typically contains private declarations, private configurations, and the definitions of functions and methods.

Header file

  • .h extension for C header files

  • .hpp extension for C++ header files

  • Store public/sharable components

    • Declarations of functions

    • Declarations of classes

    • Global variables

    • Declarations of types, type alias, macros, etc.

    • Class templates

Source code file

  • .c extension for C source files

  • .cpp extension for C++ source files

  • Store private components

    • Declarations of anything that will be only used in the current cpp file

    • Function/class method definitions (implementation)

Module

  • A module is a collection of header files and source files that are related to each other. In our class, we will use the term “module” to refer to a pair of header and source code file with the same base name. It is also called a library.

  • E.g. lib1.hpp and lib1.cpp are a module.

  • Allows multiple classes and functions in a same module.

  • Two types

    • Normal module: A pair of .hpp and .cpp files with the same base name.

    • Header-only module: A single .hpp file.

Driver

  • A driver is a program that uses the modules to perform a specific task.

  • It compiles and links the modules to create an executable.

  • Contains a main function.

  • Without header file.

  • E.g. main.cpp is a driver.

Note

C++ do not require the programmer to put only one class in a module!

Best Practices in Modular Development

Generally, it is recommended to follow these rules, especially for beginners. However, there may be situations where it is appropriate to deviate from these guidelines if there is a good reason and the programmer knows what they are doing.

  • Sort the #include directive in the following order:

    1. own header (paired header)

    2. headers in the same project

    3. third-party library headers

    4. system library headers

  • Only include headers as needed

  • Avoid include-all headers like bits/stdc++.h

  • Always add header guards

  • Minimize your header (move contents to the cpp file as much as possible)

  • Follow a consistent naming convention for all files

Pitfalls in Modular Development

Many of these pitfalls are not necessarily incorrect, but they are generally not recommended, especially for beginners. C++ is a highly flexible language, but following established conventions can make it more accessible for those who are new to programming.

  • Missing header guards in header files

  • Store all declarations in the header file even though they are not shared

  • Not following a strict naming convention of files

    • Upper case letters in the file names

    • Space and special characters in the file names

  • Use .h extension for C++ header files

  • Include cpp files like #include "lib1.cpp"

  • g++ commands with header files like g++ -std=c++14 -o main main.cpp lib1.hpp lib1.cpp