Featured image for "Get started with clang-tidy in Red Hat Enterprise Linux."

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.