Long and complex functions in 14 C++ projects
← Back to Learn · By Ronak Arora · Published · Updated
We scanned 14 widely used open-source C++ projects — 19,876 functions in all — and counted how many cross two common limits: more than 100 lines, or a cyclomatic complexity above 10. Only 240 functions (1.2%) are longer than 100 lines and 461 (2.3%) are above complexity 10. The typical function is tiny, with a median of 6 lines. And 64% of the long ones sit in tests, benchmarks, examples or other non-production paths.
So the headline is not that C++ is full of monster functions. It is a short tail, and most of it is where you would expect to find it.
How we measured
The projects are Catch2, CLI11, cxxopts, doctest, FTXUI, Google Benchmark, GoogleTest, LevelDB, nlohmann/json, RapidJSON, RE2, spdlog, yaml-cpp, {fmt}. Each was scanned at a pinned commit, listed in the open data file, using the same engine that powers the public scanner. Only C and C++ source and header files are counted.
- Length is the number of lines from a function’s first line to its closing brace, as REPO-SIGHT reports it.
- Complexity is the per-function cyclomatic complexity check, which raises a finding above 10. See cyclomatic complexity, explained for how the count works.
- Non-production means a path with a directory named like test, tests, testing, spec, docs, examples, benchmark, fuzz, third_party or vendor, or a file named like a test.
Two caveats. Test macros such as TEST_CASE are counted as function-like blocks, because that is how they behave. And some libraries ship a single-header copy of their own code, or bundle another library, so a few long functions are counted twice. About ten of the 240 sit in such copies.
The shape of the distribution
- The median function is 6 lines long. One function in a hundred is longer than about 120 lines.
- 723 functions (3.6%) are over 50 lines, 240 (1.2%) are over 100, and 97 are over 200.
- Of the 461 functions above complexity 10, 331 score 11–20, 106 score 21–50 and 24 score above 50.
Long and complex are different problems
The two limits measure different things, and it helps to see them separately. We wrote four small C++ files and ran REPO-SIGHT over each:
- A 112-line
switchwith 107 cases reports a cyclomatic complexity of 109. Both rules fire:cpp-complex-functionas a warning andcpp-long-functionas an info note. Everycaseis a separate path, so a long switch is a complex function. - The same table written as an array initializer, 114 lines of values inside a function that returns
table[k], reports a complexity of 1. Onlycpp-long-functionfires, as info. This is the kind of function that is long without being hard to follow. - A 14-line function with eleven
ifstatements joined by&&and||reports a complexity of 22. Onlycpp-complex-functionfires, as a warning. It is short and still hard to hold in your head, because each condition is another path. - A
TEST_CASEwith 130 assertions is 132 lines with a complexity of 1. It gets only the length note, and the function name is reported asTEST_CASE, which is how the test macros in the data above are counted.
Two practical points follow. First, severity differs: in the October 2026 build a complexity finding is a warning and a length finding is info. Second, the useful question is which quadrant a function is in:
- Short and simple is almost every function. In this data set the median is 6 lines.
- Long and simple is a table, a block of assertions or a sequence of similar steps. Usually leave it, or move the table into data.
- Short and complex is dense branching. Test every branch, and consider naming the conditions.
- Long and complex is the real candidate for splitting, and the first thing to look at in production code.
Where the long functions live
154 of the 240 long functions (64%) are in non-production paths, and 128 of them are test macros: 101 TEST_CASE, 13 TEST, 11 SECTION and 3 TEST_SUITE. Frameworks like Catch2 and doctest encourage putting many assertions in one block, and a block of assertions is long without being hard to follow. The longest block in the whole data set is a TEST_CASE in nlohmann/json’s unit-bjdata.cpp, at 2,967 lines.
The tail is also very uneven between projects. Here is the share of each project’s functions that are over 100 lines:
- nlohmann/json: 118 of 2,137 (5.52%)
- RE2: 23 of 964 (2.39%)
- doctest: 9 of 487 (1.85%)
- FTXUI: 14 of 1,133 (1.24%)
- RapidJSON: 22 of 2,132 (1.03%)
- cxxopts: 6 of 639 (0.94%)
- CLI11: 11 of 1,446 (0.76%)
- Google Benchmark: 4 of 573 (0.70%)
- Catch2: 10 of 1,574 (0.64%)
- spdlog: 8 of 1,389 (0.58%)
- LevelDB: 3 of 636 (0.47%)
- {fmt}: 7 of 2,192 (0.32%)
- yaml-cpp: 4 of 2,578 (0.16%)
- GoogleTest: 1 of 1,996 (0.05%)
nlohmann/json alone supplies 118 of the 240 long functions (49%), mostly because of its large test suite. GoogleTest has one in nearly 2,000. The spread between projects is more than a hundredfold, which says more about how a project is organised than about its quality.
The longest production functions
Setting tests aside, the longest functions share a pattern:
- Catch2
makeCommandLineParser, 354 lines — builds the command-line parser and its option handlers. - nlohmann/json
pow5_128, 335 lines — a table of 128-bit approximations of powers of five inpow5_table.hpp, not logic. - nlohmann/json
patch_inplace, 304 lines — applies a JSON patch document. - {fmt}
format_float, 303 lines — and spdlog bundles a 297-line copy of it. - CLI11
from_config, 285 lines — reads configuration files. - RapidJSON
ParseNumber, 281 lines — parses numbers from JSON text.
These are number formatting and parsing, configuration and command-line handling, and constant tables. Code that has to accept messy, varied input tends to grow branches, and code that holds a table tends to be long without being complicated.
Complexity tells a similar story
Of the 461 functions above complexity 10, 102 (22%) are in non-production paths, a smaller share than for length. The most complex production functions are again parsers: CLI11’s from_config at 111, RapidJSON’s ParseNumber at 95, CLI11’s _parse_arg at 88 and {fmt}’s parse_chrono_format at 82. Their branching follows the grammar of the input they read, so a high score here is a reason to test thoroughly rather than a reason to panic.
What to do with each kind
- Constant tables: move them to a
static constarray or a data file. The function shrinks to a lookup and the complexity stays at 1. - Parsers and format handlers: split along the grammar, one function per construct, and write at least one test per branch. The branching is real, so the aim is to make each piece readable and testable, not to remove it.
- Test blocks: group related assertions into helper functions or sections, so a failure points at one scenario. Do this for readability only, and do not chase the count.
- Functions that do several jobs: extract each job into a function whose name states it. This is where splitting pays off most.
What this data does not show
- It is 14 well-known projects. They are widely used libraries, and most are heavily reviewed. Application code may look different, and we have not measured it here.
- It is one commit per project. The pinned commits are in the data file, and a later commit will give different counts.
- Length includes blank lines and comments. A function is measured from its first line to its closing brace, so a function with a long comment block counts as long.
- Neither limit is a defect. 100 lines and a complexity of 10 are conventional points at which to look, not proof of a problem.
What to do with this
- Read the list by path. Look at long functions in production code first. Treat test blocks as a separate, lower-priority question.
- Ask what kind of long it is. A constant table can move into data. A parser with many branches deserves tests for each branch. A function doing several jobs should be split.
- Watch the trend, not the zero. A handful of justified outliers is normal. A rising count between scans is the signal.
Frequently asked questions
Is 100 lines too long for a C++ function?
It is a reasonable point at which to look more closely, not a rule. In this data set 1.2% of functions pass it, and many of those are tests or tables.
Why do the numbers differ from other tools?
Counts depend on definitions: whether macros count as functions, whether single-header copies are included, and what counts as a branch. We publish ours so you can check them.
Can I run this on my own project?
Yes. Paste a GitHub URL and the report lists your longest and most complex functions by file. The C++ code analyzer page explains what is checked.