C++ Beautifier & Formatter

Header dumps from a bug report, a Stack Overflow answer with every statement flattened onto one line, a colleague who writes braces the other way. Paste the source on the left and read a rebuilt version on the right, with templates, access labels and namespaces handled instead of guessed at.

CtrlEnter reformats
Sourceyour paste, untouched
Rebuiltread only, copy or download it

The angle bracket is the hard part

Every formatter that treats C++ as C with classes trips on the same character. A less-than sign opens a comparison in one line and a template argument list in the next, and only the surrounding types tell you which. Get the call wrong and std::map<std::string, int> table comes back as arithmetic with stray spaces bolted on.

The tokenizer here looks ahead from each < and asks whether a matching close arrives before a statement ends. If a semicolon, a brace, an assignment or a logical operator turns up first, the character stays a comparison. That single rule keeps if (a < b && b > c) intact while still tightening std::vector<int> into shape.

Nested arguments carry a second trap that outlived C++98. In std::map<int, std::vector<int>> the closing pair lexes as a right shift operator, which is why older compilers demanded a space between them. The lexer splits that token back into two closers when a template list is open, so both the modern spelling and the old > > spacing land in the same place.

Before and after, on the same paste

The sample loaded above is a small registry class. Here is the shape of what arrives against what leaves, with Attach braces at 4 spaces.

Pasted
template<typename T> class Registry{public:explicit Registry(std::string name):name_(std::move(name)){}
void add(const std::string&key,const T&value){items_[key]=value;}
private:std::map<std::string,T> items_;};
Rebuilt
template <typename T>class Registry {public:explicit Registry(std::string name) : name_(std::move(name)) {}
void add(const std::string &key, const T &value) {items_[key] = value;}
private:std::map<std::string, T> items_;};

Reading that output tells you what the rules are. Indentation comes from counted brace depth rather than from whatever whitespace the source arrived with. Access labels step back to the class column. The template header moves to its own line. Member initializer lists keep their colon spacing, and the lambda body opens a normal block instead of collapsing into the argument list.

Match the project, not your taste

Reformatting a file into a style the repository does not use produces a diff where every line changed and none of them meaningfully. Reviewers stop reading, git blame points at you for code you never wrote. Pick the preset the project already follows and treat the rest as a preference argument for a different day.

  • LLVMAttach braces, 2 spaces, references bound right, namespace bodies flush left. The default that clang-format ships with, and the safest pick for a fresh repository.
  • GoogleLLVM with the star and ampersand bound to the type, so const T& value. Common across Abseil, Protobuf and most Google published C++.
  • MozillaBraces break onto their own line at 2 spaces. Gecko sources and much of the Firefox tree read this way.
  • WebKit4 space indent, type bound pointers, template headers left on the declaration line. Blink and JavaScriptCore inherit it.
  • Qt4 spaces with references bound right and namespace members indented. Matches Qt Creator defaults and most KDE modules.

Switching any single control drops the preset back to Custom. Nothing is stored between visits, so a reload starts from Attach braces at 4 spaces.

Star position is a readability argument, not a style tic

Both spellings compile identically. The disagreement is about which lie is worse. char* a, b; looks like two pointers and declares one pointer plus one plain char, because the star binds to the declarator. Right binding writes it as char *a, b;, which shows the truth. Type binding argues that the pointer belongs to the type in your head and that multiple declarators on one line were the real mistake.

Pick whichever the file already uses. The setting also drives references, so const std::string &name and const std::string& name follow the same choice rather than drifting apart inside one function signature.

A related habit worth knowing about: const T *ptr and T const *ptr mean the same thing, while T *const ptr means something different. East const writers put the qualifier after the type every time so the rule reads right to left with no exception. This page preserves whichever order you wrote instead of rewriting qualifiers.

Where it stops

  • No column limit and no line wrapping. A 200 character template instantiation stays on one line.
  • Argument lists are never broken across lines or aligned to an opening parenthesis, which is most of what real clang-format spends its time on.
  • Braced initializer lists keep the line breaks you wrote, so a lookup table stays as authored.
  • Includes are neither sorted nor grouped, and no forward declarations are moved.
  • Reference detection is a heuristic. Mask & bits with an uppercase left side reads as a declaration and gets spaced like one.
  • A macro holding half a brace pair breaks depth counting from that point down. The strip under the editors reports the imbalance rather than pretending the result is sound.
  • Sources past 2 MB are refused instead of freezing the tab.

The output is meant for reading code that landed in front of you. What goes into a commit belongs to the formatter your repository already runs, because that one owns line breaking and yours will disagree.

Make the repository decide instead

Drop a config at the root, generated from whichever preset you settled on above.

# .clang-format BasedOnStyle: LLVM Standard: c++20 IndentWidth: 4 UseTab: Never ColumnLimit: 100 PointerAlignment: Right AccessModifierOffset: -4 NamespaceIndentation: All AlwaysBreakTemplateDeclarations: Yes AllowShortFunctionsOnASingleLine: Empty SortIncludes: CaseInsensitive

Then stop arguing about it in review. Format only the lines a commit touches, which keeps history readable on an old codebase:

git diff -U0 --cached | clang-format-diff -p1 -i clang-format --dry-run --Werror src/*.cpp include/*.hpp find src -name '*.cpp' -exec clang-format -i {} +

The second line is the one worth putting in continuous integration. It changes nothing and exits non zero when a file drifts, so the check fails on the pull request rather than in a rebase three weeks later.

C++ formatting questions

Is my source sent to a server?

No. The lexer and the printer are JavaScript inside this page, so the code never leaves the tab. Proprietary headers and client work are safe to paste here in a way they are not on a page that posts to an API.

Why does the output differ from clang-format run locally?

Line breaking. Real clang-format solves a layout problem across a column limit, breaking argument lists and aligning continuations. This page rebuilds indentation, brace placement and spacing while leaving your line breaks alone, so long lines survive as written.

The indentation goes wrong halfway down my file. What causes that?

An unbalanced brace, and usually one hiding inside a macro. Check the strip under the editors first. Once brace depth is wrong, every indent decision below the bad brace is built on it, so fix the source before reading the output.

Does it understand templates and lambdas?

Yes for spacing and indentation. Template argument lists are detected by scanning ahead for a matching close, nested closers written as a right shift are split apart, and lambda bodies are treated as ordinary blocks. Template metaprogramming with deeply nested conditions still reads better after clang-format.

Which preset should a new project start from?

LLVM. It is the clang-format default, most C++ developers read it without friction, and it gives you a one line .clang-format file to commit. Consistency across the repository matters more than the specific choice.

Can I get the original back after formatting?

Your paste stays untouched in the left pane. Formatting only writes to the right pane, so the two versions sit side by side until you clear or reload the page.