-
Notifications
You must be signed in to change notification settings - Fork 0
Cpp20 features
FancyArgumentParser works from C++11 onward, but a few things become available only under C++20. This page collects them; for the everyday task-oriented examples (which all work in C++11), see the Examples page.
- Consuming the library as a module
- Keyword arguments (designated initializers)
- Post-processing parsed values with std::ranges
On toolchains that support C++20 modules (MSVC, GCC ≥ 14) you can bring the whole
library in with a single import instead of #include, via the argparse.ixx
module interface shipped alongside the header:
import argparse;
#include <vector>
#include <string>
#include <iostream>
int main(int argc, char** argv)
{
argparse::ArgumentParser parser("demo");
parser.AddArgument(argparse::CreateNamedArgument("n", "name", 1,
argparse::ArgTypeCast::e_String, true));
auto obj = parser.ParseArgs(argc, argv);
if (!obj.IsArgValid())
{
std::cout << obj.GetErrorString() << "\n";
return 1;
}
std::cout << "Hello, " << obj.GetAsString("name") << "!\n";
return 0;
}The module simply wraps argparse.h and re-exports its public API, so import
and #include give you exactly the same library — the header remains fully
usable on its own. ARGPARSE_NAMESPACE_NAME works when building the module too.
Important — include order on MSVC. Put any standard-library
#includes before theimport argparse;line. Newer MSVC (VS 18 / 14.51+) reportsC2572 "redefinition of default argument"if you import first and then include the same std headers the module already pulled in. Includes-before-import avoids it.
Building it — add argparse.ixx to your build as a module interface unit.
For example with CMake (≥ 3.28) and a compiler that supports modules:
add_executable(app main.cpp)
target_sources(app
PRIVATE
FILE_SET argparse_module TYPE CXX_MODULES
BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}
FILES ${CMAKE_CURRENT_SOURCE_DIR}/argparse.ixx)
target_compile_features(app PRIVATE cxx_std_20)If your toolchain does not support modules (e.g. some Clang/AppleClang versions),
just keep using #include "argparse.h" — nothing else changes.
CreateNamedArgument and CreatePositionalArgument also accept an aggregate
spec (NamedArgSpec / PositionalArgSpec). With C++20 designated
initializers this reads like Python's add_argument(type=..., required=...):
you name each field and skip the ones you don't need, instead of remembering
positional order.
// Build with C++20: c++ -std=c++20 -I<path> greet.cpp
#include <iostream>
#include "ArgParse/argparse.h"
int main(int argc, char** argv)
{
auto parser = argparse::ArgumentParser("greet").SetDescription("Keyword-style arguments (C++20)");
parser.AddArgument(argparse::CreateNamedArgument({
.shortName = "n",
.longName = "name",
.nargs = 1,
.type = argparse::ArgTypeCast::e_String,
.required = true,
.help = "Who to greet"}));
parser.AddArgument(argparse::CreateNamedArgument({
.longName = "count",
.type = argparse::ArgTypeCast::e_int,
.required = false,
.help = "How many times"}));
auto obj = parser.ParseArgs(argc, argv);
if (!obj.IsArgValid())
{
std::cout << obj.GetErrorString() << "\n" << parser.GetHelp(80) << std::endl;
return 1;
}
auto countArg = obj.GetArg("count");
int count = countArg.GetArgumentExists() ? countArg.GetAsInt() : 1;
for (int i = 0; i < count; ++i)
std::cout << "Hello, " << obj.GetAsString("name") << "!\n";
return 0;
}>>> greet --name World --count 2
Hello, World!
Hello, World!
The full field list, in declaration order:
| Struct | Fields |
|---|---|
NamedArgSpec |
shortName, longName, nargs, type, required, help, validator, validator_message, choices, pattern, default_value |
PositionalArgSpec |
name, nargs, type, required, help, validator, validator_message, choices, pattern, default_value |
choices (a std::vector<std::string>, parsed to type), pattern (a regex),
and default_value (a std::any, C++17+) let you set those inline instead of
chaining SetChoices / SetPattern / SetDefault:
parser.AddArgument(argparse::CreateNamedArgument({
.longName = "port", .type = argparse::ArgTypeCast::e_int, .required = false,
.choices = {"80", "443", "8080"},
.default_value = 8080}));The validator / validator_message fields let you attach a validator inline
(see Choices and validators on the Examples page):
parser.AddArgument(argparse::CreateNamedArgument({
.shortName = "m", .longName = "mode", .required = false,
.validator = [](const std::string& v){ return v == "fast" || v == "safe"; },
.validator_message = "mode must be 'fast' or 'safe'"}));Notes:
- Field order in the designated initializer must follow the struct's declaration order above. Fields you omit take their defaults.
SetChoicesis not a spec field — chain it on the returned argument (with an explicitstd::vector<std::string>to pick the right overload).- The same structs also work with ordinary aggregate initialization in C++11/14/17 — just supply the leading fields positionally instead of by name.
The parser itself has a matching aggregate, ParserSpec:
auto parser = argparse::ArgumentParser(argparse::ParserSpec{
.name = "cptool",
.description = "Copy files",
.allowAbbrev = false});The value getters (GetAsVecInt() and friends) return by value, so they
compose directly with C++20 range views — no dangling, no manual copies.
// Build with C++20: c++ -std=c++20 -I<path> stats.cpp
#include <iostream>
#include <ranges>
#include <vector>
#include "ArgParse/argparse.h"
int main(int argc, char** argv)
{
auto parser = argparse::ArgumentParser("stats").SetDescription("C++20 ranges over parsed values");
parser.AddArgument(argparse::CreateNamedArgument("n", "nums",
argparse::kFromOneToInfiniteArgCount, argparse::ArgTypeCast::e_int, true)
.SetHelp("Integers to process"));
auto obj = parser.ParseArgs(argc, argv);
if (!obj.IsArgValid())
{
std::cout << obj.GetErrorString() << "\n" << parser.GetHelp(80) << std::endl;
return 1;
}
auto nums = obj.GetAsVecInt("nums");
// Keep even numbers and square them, lazily, via C++20 views.
auto evenSquares = nums
| std::views::filter([](int x) { return x % 2 == 0; })
| std::views::transform([](int x) { return x * x; });
std::cout << "even squares:";
for (int v : evenSquares)
std::cout << ' ' << v;
std::cout << std::endl;
return 0;
}>>> stats --nums 1 2 3 4 5 6
even squares: 4 16 36
Back to the Examples page.