header-warden is a cross-platform multithreaded CLI tool that identifies and reports missing standard library headers in C++ code.
In many modern programming languages, such as Python, the use of a standard library module is explicitly declared, making it clear which module is being utilized.
import typing
def foo(bar: typing.List[int]) -> None:
pass
Note: In Python >=3.9, list[]
can be used directly without using typing.List
. This is merely for illustration purposes.
However, in C++, it is possible to use a standard library function without including the corresponding header.
// #include <vector>
#include <iostream>
void foo(std::vector<int> bar) // No error or warning is generated
{
}
This is because they all share the std
namespace and headers (e.g., #include <iostream>
) can include other headers (e.g., #include <vector>
) internally. This, however, varies between compilers and platforms, which can lead to portability issues (i.e., code that compiles on one platform but not on another).
As a C++ programmer, you're supposed to memorize which standard library functions are included in which headers. If you're a perfectionist like me, this quickly becomes very tedious and error-prone.
header-warden addresses this by encouraging the explicit listing of all standard library functions as comments in the #include
directive. As a bonus, this also makes it easier to memorize the required headers for each function for beginners.
#include <algorithm> // for std::find
#include <string> // for std::string, std::to_string
#include <vector> // for std::vector
After running header-warden, you will receive a report that lists all standard library functions used in your code, along with the corresponding #include
directives. This will help you ensure that all standard library functions are correctly listed as comments after the #include
directive. Links to cppreference.com are also provided for missing functions, which makes it easy to find the correct header.
-- 1) BARE INCLUDES --
8| #include <iostream>
-> Bare include directive.
-> Add a comment to '#include <iostream>', e.g., '#include <iostream> // for std::foo, std::bar'.
-- 2) UNUSED FUNCTIONS --
11| #include <algorithm> // for std::find
-> Unused functions listed as comments.
-> Remove 'std::find' comments from '#include <algorithm> // for std::find'.
-- 3) UNLISTED FUNCTIONS --
31| std::sort(result.begin(), result.end());
-> Unlisted function.
-> Add 'std::sort' as a comment, e.g., '#include <foo> // for std::sort'.
-> Reference: https://duckduckgo.com/?sites=cppreference.com&q=std%3A%3Asort&ia=web
What you do with this information is completely up to you. You can choose to add the missing functions to the comments, or you can ignore them. The goal is to make you aware of the potential issues in your code.
- Written in modern C++ (C++17).
- Comprehensive documentation with doxygen-style comments.
- Automatic third-party dependency management using CMake's FetchContent.
- Multithreaded file processing via BS::thread_pool.
- No missing STL headers thanks to this tool (run on itself).
This project has been tested on the following systems:
- macOS 14.6 (Sonoma)
- Manjaro 24.0 (Wynsdey)
- Windows 11 23H2
Automated testing is also performed on the latest versions of macOS, GNU/Linux, and Windows using GitHub Actions.
Pre-built binaries are available for macOS (ARM64), GNU/Linux (x86_64), and Windows (x86_64). You can download the latest version from the Releases page.
To remove macOS quarantine, use the following commands:
xattr -d com.apple.quarantine header-warden-macos-arm64
chmod +x header-warden-macos-arm64
On Windows, the OS might complain about the binary being unsigned. You can bypass this by clicking on "More info" and then "Run anyway".
To build and run this project, you'll need:
- C++17 or higher
- CMake
Follow these steps to build the project:
-
Clone the repository:
git clone https://github.com/ryouze/header-warden.git
-
Generate the build system:
cd header-warden mkdir build && cd build cmake ..
Optionally, you can disable compile warnings by setting
ENABLE_COMPILE_FLAGS
toOFF
:cmake .. -DENABLE_COMPILE_FLAGS=OFF
-
Compile the project:
To compile the project, use the following command:
make
If you want to use all available cores when compiling with
make
, you can pass the-j
flag along with the number of cores available on your system:-
macOS:
make -j$(sysctl -n hw.ncpu)
-
GNU/Linux:
make -j$(nproc)
-
8-core CPU:
make -j8
-
After successful compilation, you can run the program using ./header-warden
. However, it is highly recommended to install the program, so that it can be run from any directory. Refer to the Install section below.
Note: The mode is set to Release
by default. To build in Debug
mode, use cmake .. -DCMAKE_BUILD_TYPE=Debug
.
If not already built, follow the steps in the Build section and ensure that you are in the build
directory.
To install the program, use the following command:
sudo cmake --install .
On macOS, this will install the program to /usr/local/bin
. You can then run the program from any directory using header-warden
.
To run the program, use the following command:
header-warden
The program expects at least one argument, which can be a file or directory.
header-warden src/main.cpp
header-warden src
If a directory is passed, the program will recursively search for files with the following extensions: .cpp
, .hpp
, .h
, .cxx
, .cc
, .hh
, .hxx
, .tpp
.
You can also pass multiple files and directories as arguments.
header-warden ../src ~/dev/app/tests
Note: On Windows, a modern terminal emulator like Windows Terminal is recommended, although the default Command Prompt will display UTF-8 characters correctly.
When processing three or more files, multithreading is enabled, and results are printed asynchronously as each file is processed. For 1-2 files, multithreading is disabled to avoid unnecessary overhead. Please note that if you pass a directory, more than two files might be found through recursive search, which will enable multithreading.
Within each file, however, the app analyzes lines sequentially, one at a time. While it’s technically possible to make this line-by-line analysis multithreaded, doing so would add significant complexity and overhead, as most files are analyzed in milliseconds (e.g., 25 ms in total for all files in this project). Only exceptionally large files (e.g., the 11.5MB assets.cpp
file containing embedded Korean font data in my aegyo app) might benefit from within-file multithreading. For 99% of files, however, this approach would actually reduce performance, making the current implementation more efficient for the expected use case.
With this in mind, large files always appear at the end of the output as they take longer to process, while results for smaller files are displayed immediately.
Note: The --no-multithreading
flag can be used to disable multithreading altogether, regardless of the number of files being processed.
[~] $ header-warden --help
Usage: header-warden [--help] [--version] [--no-bare] [--no-unused]
[--no-unlisted] [--no-multithreading]
paths...
Identify and report missing headers in C++ code.
Positional arguments:
paths files or directories to process [nargs: 1 or more]
Optional arguments:
-h, --help shows help message and exits
-v, --version prints version information and exits
--no-bare disables bare include directives
--no-unused disables unused functions
--no-unlisted disables unlisted functions
--no-multithreading disables multithreading
Tests are included in the project but are not built by default.
To enable and build the tests manually, run the following commands:
cmake .. -DBUILD_TESTS=ON
make
ctest --output-on-failure
All contributions are welcome.
This project is licensed under the MIT License.