Into the Future with Modules
Modules replace the traditional header-based inclusion system inherited from C. Other modern or modernized languages support modules, demonstrating their relevance and importance. Even FORTRAN, the oldest language from 1957 and my first programming language, has adopted modules.
![]() |
Another heralded feature added in C++20 is the three-way-comparison or spaceship operator( 1 IF (expression) label1, label2, label3
The label jumped to was selected by |
C++ is now catching up by introducing modules in C++20, a significant update that brings the language closer to modern programming practices.
![]() |
Exercise caution about considering a module as a quirky version of a header. This analogy is as misleading as viewing a reference as a peculiar form of a pointer. We’ve all encountered individuals who have fallen into this trap, leading to significant confusion. Modules and references, while addressing similar needs, do so in distinct, and hopefully more effective, ways. |
Working With Larger Programs
Early programs were written using punched cards and tapes, some quite large or long. As a computer operator, I fed many 2500 card trays into the reader, although many cards may have been data. Nicholas Wirth’s original Pascal compiler was a one-pass compiler in a large deck of cards.
As computer systems developed, the programs became more extensive, and using multiple decks was unwieldy. Dropping the deck or experiencing a card jam that destroyed cards was devasatting. Storing programs on tapes or disks significantly improved code management.
Monolithic programs were challenging to work with. Developers broke code into mainline, functions, procedures, and subroutines to create manageable and reusable portions. It was natural to split the humongous mess into more distinct blocks for more effective use, but doing this was a problem.
Inclusion and Separate Compilation
An early approach broke the program source code into logical divisions, including a section where needed by other code. Many languages adopted this technique, including C with headers as an inclusion mechanism.
| Language | Era | Separate Compilation? | Inclusion Mechanism |
|---|---|---|---|
| C | 1972–present | (.c files are separate units) |
#include for headers |
| FORTRAN | 1957–present | (Fortran 90+ MODULEs) (Pre-90 COMMON blocks) |
INCLUDE for old-style inclusion |
| Pascal | 1970–1990s | (UNITs enable modular compilation) |
USES clause for inclusion |
| Ada | 1980–present | (PACKAGEs have separate compilation) |
None needed |
| COBOL | 1959–present | (mostly monolithic) |
COPY for inclusion |
| PL/I | 1964–1980s | (mostly monolithic) |
INCLUDE for inclusion |
| ALGOL | 1958–1980s | (typically whole-program compilation) | Some implementations supported separate compilation |
Separate compilation occurred in a similar timeframe, with some implementations of Algol 60 using it. It wasn’t until the mid-60s that it appeared in many other languages. C used it from its origin in the early ’70s.
Encapsulation
The next problem was that programmers would basically work around restrictions. (Who would have guessed?) Instead of using functions provided to access data, they worked directly with the data, often leading to inconsistencies that introduced bugs.
Encapsulation to hide details was introduced to minimize this problem. Modularity was used to implement encapsulation. Modula-2 (1978) was the first module system, with Ada following in a few years. The C language never adopted a module system.
As the object-oriented development paradigm swept in, bringing along C++, encapsulation strengthened since a class can prevent external changes.
Comparing Modules and Headers
Headers are an incomplete solution, and even C++ classes are insufficient. Implementation details leak into headers, and large headers used in multiple translation units are processed for each translation unit, increasing compilation time with no real gain.
Header organization can be a challenge when circular dependencies occur. Guards in headers are required to prevent multiple inclusions, which lengthen build times and cause possible inconsistencies. The information in the header becomes part of the global scope/namespace, which can lead to conflicts across libraries from different sources.
As this table shows, modules are intended to address the flaws of headers. Most expect this to occur, and experience in other languages supports this expectation.
| Feature | Header Files | Modules |
|---|---|---|
| Compilation Speed | Slow, due to repeated parsing | Fast, as modules are compiled once |
| Encapsulation | Weak, everything is exposed | Strong, only exported symbols are visible |
| Dependency Management | Hard, prone to cyclic issues | Easy, avoids unnecessary dependencies |
| Global Namespace Pollution | Yes, everything leaks | No, only explicitly exported items |
| Security | Risky, macros and internals leak | Safe, controlled visibility |

