# cxxopts: a header-only C++ option parser for GNU-style command lines

> cxxopts is a single-header C++ library that parses GNU-style options, positional arguments and typed values. It suits small to mid-sized tools that want argparse-like ergonomics without a build dependency.

**jarro2783/cxxopts** — Lightweight C++ command line option parser

- Repository: https://github.com/jarro2783/cxxopts
- Stars: 4,812 · Forks: 662
- Language: C++
- License: MIT
- Published: 2026-09-23 · Updated: 2026-09-23 · Language: en
- Canonical page: https://hysenlabs.com/projects/jarro2783-cxxopts

## The problem cxxopts solves for C++ command line tools

Parsing argv by hand gets ugly fast. A tool that accepts --long, --long=argument, --long argument, -a, -ab and -abc argument needs a state machine, and the moment you add a typed value or a default you are writing a small parser library inside your program. cxxopts exists so that you do not. You declare options with a long name, an optional short name, a description and a value type, then call parse. The README describes it as "a lightweight C++ option parser library, supporting the standard GNU style syntax for options", and the repository is C++ with an MIT licence.

The audience is narrow and specific. This is for C++ projects that ship a binary with a conventional Unix command line: a compiler front end, a test runner, a data conversion tool, a game or simulation launcher. It is not a configuration framework, it does not read files, and it does not generate a shell completion script. If your program has subcommands with their own option sets, you will be building that layer yourself on top of what cxxopts gives you.

## How parsing works: declaration, parse, lookup

The data flow has three stages. First you construct a cxxopts::Options object with a program name and a one-line description. Then you call add_options and declare each option as a tuple of short name, long name, description and an optional value specification. Finally you call options.parse(argc, argv), which returns a ParseResult.

Lookup happens through the result, not the parser. result.count("option") tells you how many times an option appeared, and result["opt"].as<type>() retrieves the value. According to the README, if the option does not exist or is not of the requested type, an exception is thrown. That is a deliberate design choice: a typo in an option name becomes a runtime throw rather than a silent default.

Two behaviours matter in practice. Unrecognised arguments are rejected unless you call options.allow_unrecognised_options(), after which they are collected and returned by result.unmatched(). And positional arguments are opt-in: you declare them as ordinary options, then call options.parse_positional with the names in order, and the README notes that the call defaults to replacing the positional list, with cxxopts::PositionalMode::Append available to add to it instead.

Version 3 changed the contract in two ways the README calls out. The parser no longer modifies its arguments, so a const argc and argv can be passed and will not be changed. And ParseResult no longer depends on the parser, so it can be returned out of the scope where it was created. Both changes make the result object easier to pass around, at the cost of storing the unmatched argument list inside it.

## Installing cxxopts and running a first parse

The library is header-only, so the minimal install is copying the header into your include path. The README shows the include as <cxxopts.hpp>, and the repository keeps it under include/. Distribution packages exist: the badges in the README point at Conan, vcpkg and Homebrew, and the repository also carries CMakeLists.txt, meson.build, BUILD.bazel and a WORKSPACE file, so CMake, Meson and Bazel builds are all represented. There is an INSTALL file at the top level for build instructions.

If you use vcpkg, the package name is cxxopts:

```bash
vcpkg install cxxopts
```

With Conan the recipe is also named cxxopts. With Homebrew the formula is cxxopts. For a CMake project, the repository ships a CMakeLists.txt and a cmake/ directory, which is the usual arrangement for a find_package or add_subdirectory integration; check the INSTALL file for the target name your build should link against.

A first program follows the README's structure directly. Declare a boolean debug flag, an integer, a file name and a verbose flag with a default:

```cpp
#include <cxxopts.hpp>

int main(int argc, char** argv) {
  cxxopts::Options options("MyProgram", "One line description of MyProgram");
  options.add_options()
    ("d,debug", "Enable debugging")
    ("i,integer", "Int param", cxxopts::value<int>())
    ("f,file", "File name", cxxopts::value<std::string>())
    ("v,verbose", "Verbose output", cxxopts::value<bool>()->default_value("false"));

  auto result = options.parse(argc, argv);
  return 0;
}
```

Run it as ./MyProgram --file=in.txt -i 3 and the file option holds "in.txt". Read values back with result["file"].as<std::string>() and check presence with result.count("debug"). If you ask for a type that does not match what was declared, the README says an exception is thrown, so wrap the lookups if your program has to survive bad input gracefully.

Positional arguments need one extra call. Declare the positionals as options, then name them in order:

```cpp
options.add_options()
  ("script", "The script file to execute", cxxopts::value<std::string>())
  ("server", "The server to execute on", cxxopts::value<std::string>())
  ("filenames", "The filename(s) to process", cxxopts::value<std::vector<std::string>>());

options.parse_positional({"script", "server", "filenames"});
```

With the command line my_script.py my_server.com file1.txt file2.txt file3.txt, the README's table shows "script" holding "my_script.py", "server" holding "my_server.com" and "filenames" holding the three file names as a vector.

## Where cxxopts gets in your way

The implicit value rule is the sharpest edge. The README states that if an option has an implicit value, writing --option another will not work; you must use the equals form, --option=another or -o=another. The reason given is that no argument is required for the option, so the parser cannot tell whether the next token is a value or a separate positional. That is a defensible rule, but it means an option's accepted syntax changes depending on how you declared it, which is easy to forget when writing documentation for your own tool.

Help output is grouped but not elaborate. Options can be placed into groups by passing a group name as a string to add_options, and help prints all groups by default or a chosen subset when you pass a vector of group names. There is no mention of automatic terminal-width wrapping, coloured output or man page generation.

The exception model is also a real constraint. Errors defining options derive from cxxopts::exceptions::specification and errors parsing arguments from cxxopts::exceptions::parsing, both under cxxopts::exceptions::exception, and all define what(). If your codebase is built with exceptions disabled, or if you want parse failures returned as a value rather than thrown, cxxopts is the wrong tool and you will be fighting the design rather than using it.

Finally, the README warns that master is generally a work in progress and that you probably want a tagged release. The repository is not archived and the last push was on 2026-09-22, but that does not change the advice: pin a tag such as v3.3.1 rather than tracking the default branch.

## cxxopts vs CLI11: different weight classes

CLI11 is the comparison that comes up, and the difference is in scope rather than in syntax quality. Both parse GNU-style options, both are C++ and header-oriented. CLI11 covers a larger surface: subcommands, configuration file reading, and more built-in validation are part of its model. cxxopts stays with one flat option set, typed values, defaults, implicit values, positional arguments and help groups.

That makes the choice about what you want to maintain. With cxxopts, a subcommand layer is code you write and test. With CLI11, it is a feature you configure. The trade is that cxxopts keeps the mental model small: one Options object, one parse call, one result to query. If your program is a single-purpose tool with a dozen flags, that smaller model is easier to hold in your head. If it is a multi-command suite, the extra machinery in CLI11 is doing work you would otherwise duplicate.

A second reference point is Python's argparse, which the related searches also surface. The cxxopts API is recognisably close to that style: declare, parse, then query by name. The difference is that C++ has no dynamic typing, so every value carries an explicit cxxopts::value<T>() and every retrieval carries an explicit as<T>(), with a throw when the two disagree.

## Maintenance, licence and upgrade cost

The repository is not archived and the last push was on 2026-09-22, so the project is receiving changes. Releases are infrequent: v3.3.1 on 2025-05-26, v3.2.0 on 2024-02-15 and v3.1.1 on 2023-02-15. For a header-only parser with a settled API that cadence is reasonable, but it does mean a bug you hit may sit until the next tag. Pin a release and read CHANGELOG.md before moving between tags.

The migration cost is concentrated in one place. The README documents version 3 breaking changes for anyone coming from version 2: arguments are no longer modified, and ParseResult no longer depends on the parser, storing unmatched arguments instead. Code that relied on the parser mutating argv, or that assumed a ParseResult tied to a live parser, needs editing. New users can ignore that section entirely.

Licensing is MIT. The practical consequence is that you can ship cxxopts inside a proprietary binary, and the repository includes a LICENSE file to copy into your notices. This is a description of the licence text, not legal advice; if your organisation has a policy on third-party notices, route it through whoever owns that policy.

## Conclusion

Adopt cxxopts when you want GNU-style parsing in a single header and your option surface is small enough to declare by hand. Avoid it if you need subcommands, config-file merging or shell completion generated for you; those are outside what the README describes. Before committing, verify that your toolchain accepts the C++ standard the release you pick requires, and check the CHANGELOG for the version 3 breaking changes if you are migrating from version 2.

## FAQ

### How do I install cxxopts?

The library is header-only, so the minimum is putting cxxopts.hpp on your include path. Distribution packages exist under the name cxxopts for vcpkg, Conan and Homebrew, and the repository ships CMake, Meson and Bazel build files plus an INSTALL file.

### What are the alternatives to cxxopts?

CLI11 is the closest comparison and covers a larger surface, including subcommands and configuration file reading, while cxxopts stays with a flat option set. Python's argparse is a similar API shape in a language with dynamic typing.

### Does cxxopts work with a const argc and argv?

Yes. The README lists this as a version 3 breaking change: the parser no longer modifies its arguments, so a const argc and argv can be passed and will not be changed.

### How does cxxopts handle unrecognised arguments?

By default they are not accepted. Calling options.allow_unrecognised_options() lets both unmatched positionals and unmatched -- arguments through, and they are then retrieved with result.unmatched().

### Can cxxopts parse positional arguments?

Yes. Declare the positionals as options, then call options.parse_positional with their names in order. The call replaces the positional list by default; cxxopts::PositionalMode::Append adds to the existing list instead.

## Sources

- [Issues](https://github.com/jarro2783/cxxopts/issues)
- [jarro2783/cxxopts on GitHub](https://github.com/jarro2783/cxxopts)
- [License: MIT](https://github.com/jarro2783/cxxopts/blob/master/LICENSE)
- [README](https://github.com/jarro2783/cxxopts/blob/master/README.md)
- [Releases](https://github.com/jarro2783/cxxopts/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/jarro2783-cxxopts
