Imposing a common coding style can improve the readability and maintainability of your code in shared projects. Code consistency is particularly important in open source projects, where contributors often revise code written by others. Code styling can also be especially challenging in open source projects because different contributors have their own style preferences. This article introduces clang-format, an uncomplicated tool that you can use to set a common code style for your team projects written C, C++, and Objective C.
As an example for our discussion, I'll use xsimd, a project I contribute to, which enables the manipulation of batches of numbers with the same arithmetic operators used for single values. I will show you how to choose a code style, convert an existing codebase to that new style, and enforce the code style on future commits.
Note that clang-format
is part of the LLVM project.
Introducing clang-format
clang-format
is a tool that auto-formats C, C++, and Objective C, much as the venerable indent tool does for C. You can run the tool like this:
clang-format -i my/source/file.cpp
Executing this command will format my/source/file.cpp
according to the default style. Omitting -i
dumps the indented content on the standard output instead of modifying the input in place.
The default style is the one used by the LLVM project itself, but you can provide your own style guide in the form of a .clang-format
file. clang-format
automatically looks for a .clang-format
file in the current folder; if it cannot find one, it goes up one level in the directory tree and looks there, repeating the process until it finds one or reaches the root directory (/
). Only then does it fall back to the default LLVM style.
Among other settings, clang-format
can normalize the spacing before and after commas, positioning of brackets, alignments of arguments, and the maximum column width.
Explore your code styling options
Instead of walking through all the different formatting directives that might go into a .clang-format
file, you can use the clang-format configurator. This web application makes it possible to interactively explore different styling options and watch their impact on a codebase. Once you've found the style you like, the configurator will dump the associated configuration for you. In the case of the xsimd
project, we ended up with the following:
---
BasedOnStyle: WebKit
AlignAfterOpenBracket: Align
AlignConsecutiveDeclarations: 'false'
BreakBeforeBraces: Allman
NamespaceIndentation: All
...
Note that using the right BasedOnStyle
entry helps keep the configuration to a minimum. The configurator gives you a list of styles you can choose from for this option. For instance, the option PointerAlignment: Left|Right|Middle
lets you enforce consistent positioning for *
in pointer types, such as choosing between int* foo
, int *foo
, and int * foo
.
Normalize an existing project
Once you've chosen your code style, you need to format your codebase to comply with it. Picking a style and sticking to it from day one is probably the easiest route, but we didn't have that foresight with xsimd
. Instead, we ran a very old-school find -name '*.[ch]pp' -exec clang-format -i {} \;
to format the whole codebase, and we pushed the change in a single commit. Our approach was debatable, as it does not interact well with later git blame
calls. You can use the --ignore-revs-file option to ignore bulk reformatting commits in git blame
.
Alternatively, it is possible to only apply styling to new commits, which avoids messing with your project's history. If you go that route, the LLVM project provides a utility tool named clang-format-diff
to format patches, though that's beyond the scope of this article.
Changing the style of a codebase shouldn’t affect the build. However, it is important to understand that clang-format
may reorder includes (though that is, of course, a configurable choice). Using clang-format
could reveal hidden dependencies between headers, and thus break the build. That said, it is probably a good idea to fix implicit header dependencies, anyway.
Enforce the new coding rules
Even if you establish coding rules for a project, new contributors may not be aware of them. Mentioning clang-format
and its usage in a CONTRIBUTING.md
file is a good idea. Here is what we wrote for xsimd
:
We use clang-format to keep the coding style consistent. A .clang-format
file is shipped within the source. Feel free to use it!
Education is nice, but automation also helps. Setting up a GitHub Action to check new pull requests with clang-format
helps inform contributors—that red continuous integration icon definitely gets their attention! Here is the content of our .github/workflows/clang-format-check.yml
file:
name: clang-format
on: [push, pull_request]
jobs:
formatting-check:
name: Format check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run clang-format style check for C/C++ programs.
uses: jidicula/clang-format-action@v4.2.0
with:
clang-format-version: '13'
Conclusion
This article briefly described how we deployed clang-format
on the existing codebase of an example project, xsimd
. I covered the process from choosing a code style to converting the codebase to enforcing the code style on future commits. Once you've set this all up, you can forget formatting nits and focus on coding.
Acknowledgments
I would like to thank Konrad Kleine and Tom Stellard for reviewing this post, and Johann Mabille for our interactions on the xsimd
project.