Przejdź do głównej zawartości

Cleaning up #includes

Cleaning up includes

In previous article I described why you should care about clean #includes and what rules I suggest to achieve it. If you already have a project that doesn't follow those rules, it would be tedious manual task to implement my advises. This article describes how some tools can help you answer these questions:
  • How to replace include guards with pragma once in whole project?
  • How to sort includes in whole project?
  • How to remove redundant includes and add missing ones?
  • How to replace angle bracket includes with quotes only for project headers?

#pragma once with include-guard-convert.py

Include-guard-convert is simple tool that will find all include guards and change them to #pragma once. Tool doesn't need installation, simply download script from github and run it on your header files:
wget https://raw.githubusercontent.com/marcusmueller/include-guard-convert/master/include-guard-convert.py
chmod +x include-guard-convert.py

./include-guard-convert.py proj_path/**/*.hpp
# or
find proj/path -name "*.hpp" -exec ./include-guard-convert.py {} \;
After running this tool try to compile the project. There shouldn't be any problems. If there are, there is good chance you are doing something strange with #ifdefs or build system.
You can also verify whether all include guards where fixed, as long as you don't use #ifndef much:
grep -nr ifndef
# or if you want to ignore some directories from search:
grep -nr --exclude-dir={thirdparty,build} ifndef

Clang-format

Clang-format is popular program used to reformat multiple source files. I strongly advise you to use it at least once when adding big chunk of code to your project to achieve consistent formatting.
Today I will describe only small part of its functionality.

Installation

Clang-format is available in Ubuntu repo, but by default installs older version. You can either install default, quite recent version or the newer, but then remember that program name is clang-format-5.0 not simply clang-format.
sudo apt install clang-format-5.0

Setup

You need .clang-format configuration file in project root directory that will describe desired changes to formatting. Program may generate config with default values and you can adjust them manually.
clang-format-5.0 -style=llvm -dump-config > .clang-format
Section relevant to sorting includes looks like this:
SortIncludes:    true
IncludeCategories: 
  - Regex:           '"[^/]*"'
    Priority:        1
  - Regex:           '".*/.*"'
    Priority:        2
  - Regex:           '^((<|")(gtest|gmock|isl|json|proton|boost|glibmm|spdlog|mysqlcppcon))'
    Priority:        3
  - Regex:           '<.*>'
    Priority:        4
IncludeIsMainRegex: '$'
Documentation for other fields is available here.

Usage

Clang-format will use config file as long as it is named .clang-format and -style=file is set as parameter.
clang-format-5.0 -style=file -i proj_path/*.hpp proj_path/*.cpp
Remember that after changing order of includes you must check whether project compiles. If it doesn't, apparently some include file is missing somewhere. Most probably you will get compiler error about unknown type std::string or other. Just add missing #includes that fix the errors.

Redundant and necessary includes

Identifying necessary and redundant includes and deciding whether to include or forward declare is non-trivial task both for humans and for tools. Tool that may help with this task is Include What You Use (iwyu). It is available in Ubuntu repository ($ sudo apt get install iwyu). It is relatively easy to use with CMake-based projects:
mkdir build && cd build
cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ..
iwyu_tool -p . > iwyu_suggestions.out
fix_include < iwyu_suggestions.out 
Here option CMAKE_EXPORT_COMPILE_COMMANDS=ON creates additional file compile_commands.json. iwyu_tool.py (Ubuntu provides it as simply iwyu_tool) uses it to run iwyu program on all necessary source files (both headers and .cpp). iwyu_tool does not change files, only outputs suggested diff. You can apply it using fix_includes.py (fix_include in Ubuntu).
The program is considered alpha quality. It works for some projects, fails for other, so you must try it carefully.

Project includes with quotes

If your source code has project includes with angle brackets (<>) instead of quotes, it would be tedious to replace them by hand. If you can write a regular expression that matches only project includes, there is a one liner for you:
find proj/path/ -type f -name "*.[ch]pp" -exec sed -i 's/#include <\([A-Z].*\.hpp\)>/#include "\1"/g' {} \; 
Command reads: find all files (-type f, ignore links and directories), in given directory (proj/path/; or directories, you may enter multiple subdirectories to search only them), that have extension either .cpp or .hpp (-name "*.[ch]pp") and run on them sed program (-exec sed .... {} \;). Sed program will change files in place (-i) using regular expression ('s/from_what/to_what/g'). Regular expression will find lines matching "#include <(Capital Letter)...hpp>" and replace <> with "".
My project had it easy: all project header files start with capital letter, have .hpp extension and are in one of several directories.

I hope those tools will be helpful for you. If you think some other program should have been mentioned, don't hesitate to leave a comment :)

Komentarze

Popularne posty z tego bloga

Tidy #includes

Tidy #includes Having tidy includes in C++ project is often thought as low priority. It can however help you with making your code less fragile. This is doubly important when your project is a library that will be used by other people.
I propose that "good" includes should follow those rules:
Associated header (foo.hpp for foo.cpp) always comes firstAny file should have all the headers it needs. System headers are in <angle_brackets>, project headers are in "quotes"Project headers are first, STL headers are last, between them are libraries in order of decreasing locality.Use #pagma once instead of include guards Within groups decided by 4), alphabetic order is nice to have.  Associated header is first The header that declares class / methods should be the first to include. It also should have the same file name as current source file, except for extension. Some tools depend on this - for example IDEs use this to implement "go to declaration/implementatio…

GDB - beyond basics

GDB is console debugger that every Linux-using programmer heard about. It is not however easy to learn. Greg Law in his CppCon talk presented some of obscure, but useful features.

Text user interface Normally we use GDB with command line interface (CI). Beyond this, GDB has TUI based on Curses library. To activate it, use keyboard shortcut ctrl-x-a (hold ctrl, press x, unpress x, press a). Now you can see the code as you go through it.
ctrl-x-a - activate/deactivate TUIctrl-l - when screen gets messed up, use it to redraw. Happens when program prints to stdout/stderrctrl-p / ctrl-n - since you can't use arrows to reuse previously written command, use ctr-p/n instead of arrow up/down
ctrl-f / ctrl-n are arrows left / right
ctrl-a / ctrl-e are home / end (all those are copied from Emacs)ctrl-x-2 - second window (assembly). Shell You can run shell commands inside GDB command line. Just use keyword "shell" at the beginning. Examples:
shell psshell cat temporary_file.txtshell k…