smhk

Getting started with clang-format

Just as Black and isort can be used to format Python code, clang-format can be used to format C/C++/C# code (and more1).

Quick start §

  • Install clang-format.
  • Run clang-format -i <file> to format a single file in-place.
    • If you omit -i, the output is printed to stdout.
  • Use --style=<string> to choose from a predefined style.
    • Options are: LLVM, GNU, Google, Chromium, Microsoft, Mozilla, Webkit.
    • Preview styles here in your browser.
  • Or, define your own style in a .clang-format file.
    • Use --style=file to load a style from a .clang-format file.
      • Note that clang-format will search for a file with the exact name .clang-format in the current working directory, and if it does not find one, will go up one directoy (..), and repeat.
        • To avoid this search behaviour, and instead explicitly specify the file, use --style=file:<file path>.
    • Generate a .clang-format populated with a predefined style with: clang-format --style=Chromium --dump-config > .clang-format.
      • This will contain every single rule.
      • Alternatively, you may write your own .clang-format and only specify the rules you need.

Formatting multiple files (Bash, Linux) §

Commonly you want to format multiple source files.

Using clang-format -i *.c will only find *.c files in the current directory.

To format *.c and *.h files recursively, you can use find:

clang-format -i --style=file $(find app src include | grep -E ".*(\.c|\.h)$")

To skip any files with third_party in the filename:

clang-format -i --style=file $(find app src include | grep -E ".*(\.c|\.h)$" | grep --invert-match "third_party")

Formatting multiple files (Windows, PowerShell) §

Likewise, to run clang-format on all matching source files recursively using PowerShell, while filtering out files with third_party in the filename:

clang-format -i --style=file $(Get-ChildItem -Path $PWD/app,$PWD/src,$PWD/include -Recurse | Where Name -Match '\.(?:h|c)$' | Where FullName -NotMatch 'third_party' | Select-Object -ExpandProperty FullName)

Default style §

The default style for clang-format is LLVM. This fact is buried deep within the docs2, inside the description for RawStringFormats:

If BasedOnStyle is not found, the formatting is based on LLVM style.

This is further confirmed by this line within clang/lib/Format/Format.cpp:

const char *DefaultFallbackStyle = "LLVM";

Sorting includes §

The styles LLVM and Chromium will sort includes by default, but the other styles will not.

Sorting includes is enabled by setting SortIncludes to CaseSensitive or CaseInsensitive (see the docs for the difference). It is disabled with Never.

Beware that naïvely sorting includes can have unintended side-effects, but using IncludeCategories gives you control over how the includes are ordered. So if the include order matters, as long as you correctly configure IncludeCategories, you can still enable SortIncludes.

Beware that, when sorting includes, the whitespace between #include lines is potentially significant, for two reasons:

  • The IncludeBlocks option configures how clang-format handles whitespace between otherwise consecutive lines of #includes.
  • If #includes are separated by something else (e.g. an #ifdef) then clang-format will handle each block of includes separately. (Or perhaps it just does the first block? TBC).

There is special handling for the main #include for a source file (e.g. the main #include for "foo/widget.c" is "foo/widget.h"):

  • clang-format will attempt to find the main include for the file.
  • The regex defined by IncludeIsMainRegex is used to determine whether an #include is main, and if so, it is given priority 0 (i.e. highest, go to the top).
    • For the LLVM style, this defaults to (Test)?$, which would permit the main include for both "Widget.c" and "WidgetTest.c" to be "Widget.h".
    • For the Google style, this defaults to ([-_](test|unittest))?$, which would permit the main include for "widget-test.c, "widget_test.c", "widget-unittest.c" and "widget_unittest.c" to be "widget.h".
  • All #includes are considered for being the main include. The deciding function strips off the surrounding "" or <> from the include, and then checks that matches the filename. If a match is found, it is then put through the regex defined in IncludeIsMainRegex. If that passes, only then is it considered a main include.

Install clang-format §

Install clang-format on Windows §

Go to the LLVM releases page and download the Windows LLVM installer.

For example, at time of writing v18.1.8 is the latest release. The download named clang+llvm-18.1.8-x86_64-pc-windows-msvc.tar.xz includes LLVM and clang (and clang-format).

Install clang-format on Debian/Ubuntu §

It’s as easy as:

sudo apt install clang-format

Example .clang-format §

Following is an example of a hand-written .clang-format. It’s written in YAML:

---
BasedOnStyle: LLVM

# Alignment.
AlignAfterOpenBracket: AlwaysBreak
AlignConsecutiveMacros: true
AlignConsecutiveAssignments: true
AlignConsecutiveDeclarations: true
AlignEscapedNewlines: Right
AlignOperands: true
AlignTrailingComments: true

# Allow one-liners for case labels.
AllowShortCaseLabelsOnASingleLine: true

# Indentation.
IndentWidth: 4
ContinuationIndentWidth: 4
UseTab: Never

# Always list each argument on its own line.
BinPackArguments: false
BinPackParameters: false
ExperimentalAutoDetectBinPacking: false
AllowAllParametersOfDeclarationOnNextLine: false

# Spaces.
SpaceAfterCStyleCast: false
SpaceBeforeParens: ControlStatements
SpacesBeforeTrailingComments: 2
SpacesInCStyleCastParentheses: false
SpacesInContainerLiterals: false
SpacesInSquareBrackets: false

# Breaks.
BreakBeforeBraces: Mozilla
BreakBeforeBinaryOperators: NonAssignment

# Sorting includes.
# * The main header gets priority 0, and goes at the top. The main header is
#   essentally any header file which corresponds to the current source file
#   (e.g "foo.h" is the main header for "foo.c"). However, only header files
#   that match the regex in `IncludeIsMainRegex` are considered.
# * The "log/mod/blah.h" headers get priority 1.
# * The "blah.h" headers get priority 2.
# * The <blah.h> headers get priority 3.
SortIncludes: CaseSensitive
IncludeBlocks: Regroup
IncludeIsMainRegex: '$?'
IncludeCategories:
  - Regex: '^"log/mod/.*"'
    Priority: 1
  - Regex: '^".*"'
    Priority: 2
  - Regex: '^<.*>'
    Priority: 3

# Misc.
IndentCaseLabels: true
ReflowComments: true

For a guide as to what each rule means, try this tool, and click the blue information icon next to the rule in question.


  1. The current list of supported languages is: C, C++, Java, JavaScript, JSON, Objective-C, Protobuf and C#. ↩︎

  2. Thanks to Louis Langholtz for spotting this↩︎