This commit is contained in:
2026-09-13 13:30:21 +08:00
commit a6bbd520cf
2744 changed files with 1598795 additions and 0 deletions
+317
View File
@@ -0,0 +1,317 @@
function(add_cli_exe T)
add_executable(${T} ${ARGN})
target_link_libraries(${T} PUBLIC CLI11)
set_property(TARGET ${T} PROPERTY FOLDER "Examples")
if(CLI11_FORCE_LIBCXX)
set_property(
TARGET ${T}
APPEND_STRING
PROPERTY LINK_FLAGS -stdlib=libc++)
target_link_libraries(${T} PUBLIC c++abi)
endif()
endfunction()
if(CLI11_BUILD_EXAMPLES_JSON AND CMAKE_VERSION VERSION_GREATER "3.14.7")
message(STATUS "Using nlohmann/json")
FetchContent_Declare(
json
URL https://github.com/nlohmann/json/releases/download/v3.7.3/include.zip
URL_HASH "SHA256=87b5884741427220d3a33df1363ae0e8b898099fbc59f1c451113f6732891014")
FetchContent_MakeAvailable(json)
add_cli_exe(json json.cpp)
target_include_directories(json PUBLIC SYSTEM "${json_SOURCE_DIR}/single_include")
add_test(NAME json_config_out COMMAND json --item 2)
set_property(TEST json_config_out PROPERTY PASS_REGULAR_EXPRESSION
[=[\{.*"item": "2".*"simple": false.*\}]=])
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/input.json" [=[{"item":3,"simple":false}]=])
add_test(NAME json_config_in COMMAND json --config "${CMAKE_CURRENT_BINARY_DIR}/input.json")
set_property(TEST json_config_in PROPERTY PASS_REGULAR_EXPRESSION
[=[\{.*"item": "3".*"simple": false.*\}]=])
endif()
add_cli_exe(simple simple.cpp)
add_test(NAME simple_basic COMMAND simple)
add_test(NAME simple_all COMMAND simple -f filename.txt -c 12 --flag --flag -d 1.2)
set_property(
TEST simple_all
PROPERTY
PASS_REGULAR_EXPRESSION
[=[Working on file: filename.txt, direct count: 1, opt count: 1.*Working on count: 12, direct count: 1, opt count: 1.*Received flag: 2 \(2\) times.*Some value: 1\.2]=]
)
add_test(NAME simple_version COMMAND simple --version)
set_property(TEST simple_version PROPERTY PASS_REGULAR_EXPRESSION "${CLI11_VERSION}")
add_cli_exe(subcommands subcommands.cpp)
add_test(NAME subcommands_none COMMAND subcommands)
set_property(TEST subcommands_none PROPERTY PASS_REGULAR_EXPRESSION "A subcommand is required")
add_test(
NAME subcommands_all
COMMAND subcommands --random start --file
name stop --count)
set_property(
TEST subcommands_all
PROPERTY
PASS_REGULAR_EXPRESSION
[=[Working on --file from start: name.*Working on --count from stop: 1, direct count: 1.*Count of --random flag: 1.*Subcommand: start.*Subcommand: stop]=]
)
add_cli_exe(subcom_partitioned subcom_partitioned.cpp)
add_test(NAME subcom_partitioned_none COMMAND subcom_partitioned)
set_property(
TEST subcom_partitioned_none
PROPERTY PASS_REGULAR_EXPRESSION
[=[--file is required.*Run with --help for more information\..*This is a timer:]=])
add_test(NAME subcom_partitioned_all COMMAND subcom_partitioned --file this --count --count -d 1.2)
set_property(
TEST subcom_partitioned_all
PROPERTY
PASS_REGULAR_EXPRESSION
[=[Working on file: this, direct count: 1, opt count: 1.*Working on count: 2, direct count: 2, opt count: 2.*Some value: 1\.2.*This is a timer:]=]
)
# test shows that the help prints out for unnamed subcommands
add_test(NAME subcom_partitioned_help COMMAND subcom_partitioned --help)
set_property(
TEST subcom_partitioned_help
PROPERTY PASS_REGULAR_EXPRESSION
[=[-f,[ \t]*--file TEXT REQUIRED(.|[\r\n])*-d,[ \t]*--double FLOAT]=])
####################################################
add_cli_exe(config_app config_app.cpp)
add_test(NAME config_app1 COMMAND config_app -p)
set_property(TEST config_app1 PROPERTY PASS_REGULAR_EXPRESSION "file=")
add_test(NAME config_app2 COMMAND config_app -p -f /)
set_property(TEST config_app2 PROPERTY PASS_REGULAR_EXPRESSION "file=\"/\"")
add_test(NAME config_app3 COMMAND config_app -f "" -p)
set_property(TEST config_app3 PROPERTY PASS_REGULAR_EXPRESSION "file=\"\"")
add_test(NAME config_app4 COMMAND config_app -f "/" -p)
set_property(TEST config_app4 PROPERTY PASS_REGULAR_EXPRESSION "file=\"/\"")
####################################################
add_cli_exe(option_groups option_groups.cpp)
add_test(NAME option_groups_missing COMMAND option_groups)
set_property(TEST option_groups_missing PROPERTY PASS_REGULAR_EXPRESSION
[=[Exactly 1 option from(.|[\r\n])*is required]=])
add_test(NAME option_groups_extra COMMAND option_groups --csv --binary)
set_property(TEST option_groups_extra PROPERTY PASS_REGULAR_EXPRESSION "but 2 were given")
add_test(NAME option_groups_extra2 COMMAND option_groups --csv --address "192.168.1.1" -o
"test.out")
set_property(TEST option_groups_extra2 PROPERTY PASS_REGULAR_EXPRESSION "at most 1")
add_cli_exe(positional_arity positional_arity.cpp)
add_test(NAME positional_arity1 COMMAND positional_arity one)
set_property(TEST positional_arity1 PROPERTY PASS_REGULAR_EXPRESSION "File 1 = one")
add_test(NAME positional_arity2 COMMAND positional_arity one two)
set_property(TEST positional_arity2 PROPERTY PASS_REGULAR_EXPRESSION
[=[File 1 = one.*File 2 = two]=])
add_test(NAME positional_arity3 COMMAND positional_arity 1 2 one)
set_property(TEST positional_arity3 PROPERTY PASS_REGULAR_EXPRESSION "File 1 = one")
add_test(NAME positional_arity_fail COMMAND positional_arity 1 one two)
set_property(TEST positional_arity_fail PROPERTY PASS_REGULAR_EXPRESSION "Could not convert")
add_cli_exe(positional_validation positional_validation.cpp)
add_test(NAME positional_validation1 COMMAND positional_validation one)
set_property(TEST positional_validation1 PROPERTY PASS_REGULAR_EXPRESSION "File 1 = one")
add_test(NAME positional_validation2 COMMAND positional_validation one 1 2 two)
set_property(TEST positional_validation2 PROPERTY PASS_REGULAR_EXPRESSION
[=[File 1 = one.*File 2 = two]=])
add_test(NAME positional_validation3 COMMAND positional_validation 1 2 one)
set_property(TEST positional_validation3 PROPERTY PASS_REGULAR_EXPRESSION "File 1 = one")
add_test(NAME positional_validation4 COMMAND positional_validation 1 one two 2)
set_property(TEST positional_validation4 PROPERTY PASS_REGULAR_EXPRESSION
[=[File 1 = one.*File 2 = two]=])
if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
OR NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
#this test doesn't work without get_time which isn't available until GCC 5.0
add_cli_exe(date_validator date_validator.cpp)
add_test(NAME date_validator1 COMMAND date_validator --time 2025-08-14T14:30:00)
set_property(TEST date_validator1 PROPERTY PASS_REGULAR_EXPRESSION "date given")
add_test(NAME date_validator2 COMMAND date_validator --time 8/17/2025T14:30:00)
set_property(TEST date_validator2 PROPERTY PASS_REGULAR_EXPRESSION "Failed to parse time string")
endif()
add_cli_exe(custom_validator custom_validator.cpp)
add_test(NAME custom_validator1 COMMAND custom_validator --number 12)
set_property(TEST custom_validator1 PROPERTY PASS_REGULAR_EXPRESSION "value not within range")
add_test(NAME custom_validator2 COMMAND custom_validator --number 12 --center 10 --range 3)
set_property(TEST custom_validator2 PROPERTY PASS_REGULAR_EXPRESSION "number 12 in range")
add_cli_exe(shapes shapes.cpp)
add_test(NAME shapes_all COMMAND shapes circle 4.4 circle 10.7 rectangle 4 4 circle 2.3 triangle
4.5 ++ rectangle 2.1 ++ circle 234.675)
set_property(
TEST shapes_all
PROPERTY
PASS_REGULAR_EXPRESSION
[=[circle2 with radius 10\.7(.|[\r\n])*triangle1 with sides \[4\.5\](.|[\r\n])*rectangle2 with edges \[2\.1,2\.1\](.|[\r\n])*circle4 with radius 234\.675]=]
)
add_cli_exe(ranges ranges.cpp)
add_test(NAME ranges_range COMMAND ranges --range 1 2 3)
set_property(TEST ranges_range PROPERTY PASS_REGULAR_EXPRESSION "[2:1:3]")
add_test(NAME ranges_minmax COMMAND ranges --min 2 --max 3)
set_property(TEST ranges_minmax PROPERTY PASS_REGULAR_EXPRESSION "[2:1:3]")
add_test(NAME ranges_error COMMAND ranges --min 2 --max 3 --step 1 --range 1 2 3)
set_property(TEST ranges_error PROPERTY PASS_REGULAR_EXPRESSION "Exactly 1 option from")
add_cli_exe(validators validators.cpp)
add_test(NAME validators_help COMMAND validators --help)
set_property(
TEST validators_help
PROPERTY
PASS_REGULAR_EXPRESSION
[=[ -f,[ \t]*--file TEXT:FILE.*File name.* -v,[ \t]*--value INT:INT in \[3 - 6\].*Value in range]=]
)
add_test(NAME validators_file COMMAND validators --file nonex.xxx)
set_property(
TEST validators_file
PROPERTY PASS_REGULAR_EXPRESSION
[=[--file: File does not exist: nonex\.xxx.*Run with --help for more information\.]=])
add_test(NAME validators_plain COMMAND validators --value 9)
set_property(
TEST validators_plain
PROPERTY PASS_REGULAR_EXPRESSION
[=[--value: Value 9 not in range \[3 - 6\].*Run with --help for more information\.]=])
add_cli_exe(groups groups.cpp)
add_test(NAME groups_none COMMAND groups)
set_property(
TEST groups_none
PROPERTY PASS_REGULAR_EXPRESSION
[=[--file is required.*Run with --help for more information\..*This is a timer:]=])
add_test(NAME groups_all COMMAND groups --file this --count --count -d 1.2)
set_property(
TEST groups_all
PROPERTY
PASS_REGULAR_EXPRESSION
[=[Working on file: this, direct count: 1, opt count: 1.*Working on count: 2, direct count: 2, opt count: 2.*Some value: 1\.2.*This is a timer:]=]
)
add_cli_exe(inter_argument_order inter_argument_order.cpp)
add_test(NAME inter_argument_order COMMAND inter_argument_order --foo 1 2 3 --x --bar 4 5 6 --z
--foo 7 8)
set_property(
TEST inter_argument_order
PROPERTY
PASS_REGULAR_EXPRESSION
[=[foo : 1
foo : 2
foo : 3
bar : 4
bar : 5
bar : 6
foo : 7
foo : 8]=])
add_cli_exe(prefix_command prefix_command.cpp)
add_test(NAME prefix_command COMMAND prefix_command -v 3 2 1 -- other one two 3)
set_property(
TEST prefix_command PROPERTY PASS_REGULAR_EXPRESSION
[=[Prefix: 3 : 2 : 1.*Remaining commands: other one two 3]=])
#just test a minimal build
add_cli_exe(minimal minimal.cpp)
add_test(NAME minimal COMMAND minimal)
add_cli_exe(arg_capture arg_capture.cpp)
add_test(NAME arg_capture COMMAND arg_capture -v 27 --sub -v 13 --val prefix)
set_property(TEST arg_capture PROPERTY PASS_REGULAR_EXPRESSION "value=27")
add_test(NAME arg_capture2 COMMAND arg_capture -v 27 --sub -v 13 --val prefix)
set_property(TEST arg_capture2 PROPERTY PASS_REGULAR_EXPRESSION "after Args:-v 13 --val prefix")
add_cli_exe(callback_passthrough callback_passthrough.cpp)
add_test(NAME callback_passthrough1 COMMAND callback_passthrough --argname t2 --t2 test)
set_property(TEST callback_passthrough1 PROPERTY PASS_REGULAR_EXPRESSION "the value is now test")
add_test(NAME callback_passthrough2 COMMAND callback_passthrough --arg EEEK --argname arg)
set_property(TEST callback_passthrough2 PROPERTY PASS_REGULAR_EXPRESSION "the value is now EEEK")
add_cli_exe(enum enum.cpp)
add_test(NAME enum_pass COMMAND enum -l 1)
add_test(NAME enum_fail COMMAND enum -l 4)
set_property(
TEST enum_fail
PROPERTY PASS_REGULAR_EXPRESSION
[=[--level: Check 4 value in \{.*FAILED.*Run with --help for more information\.]=])
add_cli_exe(enum_ostream enum_ostream.cpp)
add_test(NAME enum_ostream_pass COMMAND enum_ostream --level medium)
set_property(TEST enum_ostream_pass PROPERTY PASS_REGULAR_EXPRESSION "Enum received: Medium")
add_cli_exe(digit_args digit_args.cpp)
add_test(NAME digit_args COMMAND digit_args -h)
set_property(TEST digit_args PROPERTY PASS_REGULAR_EXPRESSION "-3{3}")
add_cli_exe(modhelp modhelp.cpp)
add_test(NAME modhelp COMMAND modhelp -a test -h)
set_property(TEST modhelp PROPERTY PASS_REGULAR_EXPRESSION "Option -a string in help: test")
add_cli_exe(array_option array_option.cpp)
add_test(NAME array_option COMMAND array_option --a 1 2)
set_property(TEST array_option PROPERTY PASS_REGULAR_EXPRESSION "pass")
add_subdirectory(subcom_in_files)
add_test(NAME subcom_in_files COMMAND subcommand_main subcommand_a -f this.txt --with-foo)
set_property(TEST subcom_in_files PROPERTY PASS_REGULAR_EXPRESSION
[=[Working on file: this\.txt.*Using foo!]=])
add_cli_exe(formatter formatter.cpp)
add_cli_exe(nested nested.cpp)
add_cli_exe(subcom_help subcom_help.cpp)
add_test(NAME subcom_help_normal COMMAND subcom_help sub --help)
add_test(NAME subcom_help_reversed COMMAND subcom_help --help sub)
add_cli_exe(retired retired.cpp)
add_test(NAME retired_retired_test COMMAND retired --retired_option)
add_test(NAME retired_retired_test2 COMMAND retired --retired_option 567)
add_test(NAME retired_retired_test3 COMMAND retired --retired_option2 567 689 789)
add_test(NAME retired_deprecated COMMAND retired --deprecate 19 20)
set_property(TEST retired_retired_test PROPERTY PASS_REGULAR_EXPRESSION "WARNING.*retired")
set_property(TEST retired_retired_test2 PROPERTY PASS_REGULAR_EXPRESSION "WARNING.*retired")
set_property(TEST retired_retired_test3 PROPERTY PASS_REGULAR_EXPRESSION "WARNING.*retired")
set_property(TEST retired_deprecated PROPERTY PASS_REGULAR_EXPRESSION "deprecated.*not_deprecated")
if(CMAKE_CXX_STANDARD GREATER 13)
add_cli_exe(close_match close_match.cpp)
add_test(NAME close_match_test COMMAND close_match i)
add_test(NAME close_match_test2 COMMAND close_match upg)
add_test(NAME close_match_test3 COMMAND close_match rem)
add_test(NAME close_match_test4 COMMAND close_match upgrde)
set_property(TEST close_match_test PROPERTY PASS_REGULAR_EXPRESSION "install")
set_property(TEST close_match_test2 PROPERTY PASS_REGULAR_EXPRESSION "upgrade")
set_property(TEST close_match_test3 PROPERTY PASS_REGULAR_EXPRESSION "remove")
set_property(TEST close_match_test4 PROPERTY PASS_REGULAR_EXPRESSION "closest match is upgrade")
endif()
#--------------------------------------------
add_cli_exe(custom_parse custom_parse.cpp)
add_test(NAME cp_test COMMAND custom_parse --dv 1.7)
set_property(TEST cp_test PROPERTY PASS_REGULAR_EXPRESSION "called correct")
#-----------------------------------------------------
add_cli_exe(help_usage help_usage.cpp)
add_test(NAME help_use COMMAND help_usage --help)
set_property(TEST help_use PROPERTY PASS_REGULAR_EXPRESSION "[1..9]")
#------------------------------------------------
# This executable is for manual testing and is expected to change regularly
add_cli_exe(tester testEXE.cpp)
+34
View File
@@ -0,0 +1,34 @@
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
// Code modified from https://github.com/CLIUtils/CLI11/issues/559
#include <CLI/CLI.hpp>
#include <iostream>
#include <string>
/** This example demonstrates the use of `prefix_command` on a subcommand
to capture all subsequent arguments along with an alias to make it appear as a regular options.
All the values after the "sub" or "--sub" are available in the remaining() method.
*/
int main(int argc, const char *argv[]) {
int value{0};
CLI::App app{"Test App"};
app.add_option("-v", value, "value");
auto *subcom = app.add_subcommand("sub", "")->prefix_command();
subcom->alias("--sub");
CLI11_PARSE(app, argc, argv);
std::cout << "value=" << value << '\n';
std::cout << "after Args:";
for(const auto &aarg : subcom->remaining()) {
std::cout << aarg << " ";
}
std::cout << '\n';
}
+21
View File
@@ -0,0 +1,21 @@
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
// Code modified from Loic Gouarin(https://github.com/gouarin) https://github.com/CLIUtils/CLI11/issues/1135
#include <CLI/CLI.hpp>
#include <array>
#include <iostream>
int main(int argc, char **argv) {
std::array<int, 2> a{0, 1};
CLI::App app{"My app"};
app.add_option("--a", a, "an array")->capture_default_str();
app.parse(argc, argv);
std::cout << "pass\n";
return 0;
}
@@ -0,0 +1,28 @@
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#include <CLI/CLI.hpp>
#include <iostream>
#include <string>
int main(int argc, char **argv) {
CLI::App app("callback_passthrough");
app.allow_extras();
std::string argName;
std::string val;
app.add_option("--argname", argName, "the name of the custom command line argument");
app.callback([&app, &val, &argName]() {
if(!argName.empty()) {
CLI::App subApp;
subApp.add_option("--" + argName, val, "custom argument option");
subApp.parse(app.remaining_for_passthrough());
}
});
CLI11_PARSE(app, argc, argv);
std::cout << "the value is now " << val << '\n';
}
+115
View File
@@ -0,0 +1,115 @@
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
// Code inspired by discussion from https://github.com/CLIUtils/CLI11/issues/1149
#include <algorithm>
#include <iostream>
#include <numeric>
#include <string>
#include <utility>
#include <vector>
#include <CLI/CLI.hpp>
// only works with C++14 or higher
// Levenshtein distance function code generated by chatgpt/copilot
std::size_t levenshteinDistance(const std::string &s1, const std::string &s2) {
std::size_t len1 = s1.size(), len2 = s2.size();
if(len1 == 0 || len2 == 0) {
return (std::max)(len1, len2);
}
std::vector<std::size_t> prev(len2 + 1), curr(len2 + 1);
std::iota(prev.begin(), prev.end(), 0); // Fill prev with {0, 1, ..., len2}
for(std::size_t ii = 1; ii <= len1; ++ii) {
curr[0] = ii;
for(std::size_t jj = 1; jj <= len2; ++jj) {
// If characters match, no substitution cost; otherwise, cost is 1.
std::size_t cost = (s1[ii - 1] == s2[jj - 1]) ? 0 : 1;
// Compute the minimum cost between:
// - Deleting a character from `s1` (prev[jj] + 1)
// - Inserting a character into `s1` (curr[jj - 1] + 1)
// - Substituting a character (prev[jj - 1] + cost)
curr[jj] = (std::min)({prev[jj] + 1, curr[jj - 1] + 1, prev[jj - 1] + cost});
}
prev = std::exchange(curr, prev); // Swap vectors efficiently
}
return prev[len2];
}
// Finds the closest string from a list (modified from chat gpt code)
std::pair<std::string, std::size_t> findClosestMatch(const std::string &input,
const std::vector<std::string> &candidates) {
std::string closest;
std::size_t minDistance{std::string::npos};
for(const auto &candidate : candidates) {
std::size_t distance = levenshteinDistance(input, candidate);
if(distance < minDistance) {
minDistance = distance;
closest = candidate;
}
}
return {closest, minDistance};
}
void addSubcommandCloseMatchDetection(CLI::App *app, std::size_t minDistance = 3) {
// if extras are not allowed then there will be no remaining
app->allow_extras(true);
// generate a list of subcommand names
auto subs = app->get_subcommands(nullptr);
CLI::results_t list;
for(const auto *sub : subs) {
if(!sub->get_name().empty()) {
list.emplace_back(sub->get_name());
}
const auto &aliases = sub->get_aliases();
if(!aliases.empty()) {
list.insert(list.end(), aliases.begin(), aliases.end());
}
}
// add a callback that runs before a final callback and loops over the remaining arguments for subcommands
app->parse_complete_callback([app, minDistance, list = std::move(list)]() {
for(auto &extra : app->remaining()) {
if(!extra.empty() && extra.front() != '-') {
auto closest = findClosestMatch(extra, list);
if(closest.second <= minDistance) {
std::cout << "unmatched command \"" << extra << "\", closest match is " << closest.first << "\n";
}
}
}
});
}
/** This example demonstrates the use of close match detection to detect invalid commands that are close matches to
* existing ones
*/
int main(int argc, const char *argv[]) {
int value{0};
CLI::App app{"App for testing prefix matching and close string matching"};
// turn on prefix matching
app.allow_subcommand_prefix_matching();
app.add_option("-v", value, "value");
app.add_subcommand("install", "");
app.add_subcommand("upgrade", "");
app.add_subcommand("remove", "");
app.add_subcommand("test", "");
// enable close matching for subcommands
addSubcommandCloseMatchDetection(&app, 5);
CLI11_PARSE(app, argc, argv);
auto subs = app.get_subcommands();
for(const auto &sub : subs) {
std::cout << sub->get_name() << "\n";
}
return 0;
}
+50
View File
@@ -0,0 +1,50 @@
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#include <CLI/CLI.hpp>
#include <iostream>
#include <string>
int main(int argc, char **argv) {
CLI::App app("configuration print example");
app.add_flag("-p,--print", "Print configuration and exit")->configurable(false); // NEW: print flag
std::string file;
CLI::Option *opt = app.add_option("-f,--file,file", file, "File name")
->capture_default_str()
->run_callback_for_default(); // NEW: capture_default_str()
int count{0};
CLI::Option *copt =
app.add_option("-c,--count", count, "Counter")->capture_default_str(); // NEW: capture_default_str()
int v{0};
CLI::Option *flag = app.add_flag("--flag", v, "Some flag that can be passed multiple times")
->capture_default_str(); // NEW: capture_default_str()
double value{0.0}; // = 3.14;
app.add_option("-d,--double", value, "Some Value")->capture_default_str(); // NEW: capture_default_str()
app.get_config_formatter_base()->quoteCharacter('"', '"');
CLI11_PARSE(app, argc, argv);
if(app.get_option("--print")->as<bool>()) { // NEW: print configuration and exit
std::cout << app.config_to_str(true, false);
return 0;
}
std::cout << "Working on file: " << file << ", direct count: " << app.count("--file")
<< ", opt count: " << opt->count() << '\n';
std::cout << "Working on count: " << count << ", direct count: " << app.count("--count")
<< ", opt count: " << copt->count() << '\n';
std::cout << "Received flag: " << v << " (" << flag->count() << ") times\n";
std::cout << "Some value: " << value << '\n';
return 0;
}
+40
View File
@@ -0,0 +1,40 @@
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
// from Issue #566 on github https://github.com/CLIUtils/CLI11/issues/566
#include <CLI/CLI.hpp>
#include <iostream>
#include <sstream>
#include <string>
// example file to demonstrate a custom lexical cast function
template <class T = int> struct Values {
T a;
T b;
T c;
};
// in C++20 this is constructible from a double due to the new aggregate initialization in C++20.
using DoubleValues = Values<double>;
// the lexical cast operator should be in the same namespace as the type for ADL to work properly
bool lexical_cast(const std::string &input, Values<double> & /*v*/) {
std::cout << "called correct lexical_cast function ! val: " << input << '\n';
return true;
}
DoubleValues doubles;
void argparse(CLI::Option_group *group) { group->add_option("--dv", doubles)->default_str("0"); }
int main(int argc, char **argv) {
CLI::App app;
argparse(app.add_option_group("param"));
CLI11_PARSE(app, argc, argv);
return 0;
}
+56
View File
@@ -0,0 +1,56 @@
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#include <CLI/CLI.hpp>
#include <iostream>
#include <memory>
#include <string>
template <typename T> class DeltaRange : public CLI::Validator {
public:
T center_point;
T delta;
DeltaRange(const T &center, const T &range)
: CLI::Validator(
[this](const std::string &value) -> std::string {
T newValue;
auto result = CLI::detail::lexical_cast(value, newValue);
if(!(result && this->check(newValue))) {
return std::string("value not within range");
}
return std::string{};
},
"RANGE"),
center_point(center), delta(range) {}
CLI11_NODISCARD bool check(const T &test) const {
return (test >= (center_point - delta)) && (test <= (center_point + delta));
}
CLI11_NODISCARD T center() const { return center_point; }
CLI11_NODISCARD T range() const { return delta; }
void center(const T &value) { center_point = value; }
void range(const T &value) { delta = value; }
};
int main(int argc, char **argv) {
/* this application creates custom validator which is a range center+/- range The center and range can be defined by
* other command line options and are updated dynamically
*/
CLI::App app("custom range validator");
std::string value;
auto dr = std::make_shared<DeltaRange<int>>(7, 3);
app.add_option("--number", value, "enter value in the related range")->check(dr)->required();
app.add_option_function<int>("--center", [&dr](int new_center) { dr->center(new_center); })->trigger_on_parse();
app.add_option_function<int>("--range", [&dr](int new_range) { dr->range(new_range); })->trigger_on_parse();
CLI11_PARSE(app, argc, argv);
std::cout << "number " << value << " in range = " << dr->center() << " +/- " << dr->range() << '\n';
return 0;
}
+40
View File
@@ -0,0 +1,40 @@
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#include <CLI/CLI.hpp>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
// Custom validator is an alias of Validator, the constructor takes a function that takes as input and returns a string
const CLI::CustomValidator ISO8601(
[](std::string &input) {
std::tm tm = {};
std::istringstream ss(input);
ss >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%S");
if(ss.fail()) {
return std::string("Failed to parse time string");
}
return std::string{};
},
"datetime[%Y-%m-%dT%H:%M:%S]");
int main(int argc, char **argv) {
CLI::App app("custom validator testing");
std::string value;
app.add_option("--time", value, "enter a date in iso8601 format")->check(ISO8601)->required();
CLI11_PARSE(app, argc, argv);
std::cout << "date given = " << value << '\n';
return 0;
}
+21
View File
@@ -0,0 +1,21 @@
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#include <CLI/CLI.hpp>
#include <iostream>
int main(int argc, char **argv) {
CLI::App app;
int val{0};
// add a set of flags with default values associate with them
app.add_flag("-1{1},-2{2},-3{3},-4{4},-5{5},-6{6}, -7{7}, -8{8}, -9{9}", val, "compression level");
CLI11_PARSE(app, argc, argv);
std::cout << "value = " << val << '\n';
return 0;
}
+35
View File
@@ -0,0 +1,35 @@
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#define CLI11_ENABLE_EXTRA_VALIDATORS 1
#include <CLI/CLI.hpp>
#include <iostream>
#include <map>
#include <string>
// NOLINTNEXTLINE
enum class Level : int { High, Medium, Low };
int main(int argc, char **argv) {
CLI::App app;
Level level{Level::Low};
// specify string->value mappings
std::map<std::string, Level> map{{"high", Level::High}, {"medium", Level::Medium}, {"low", Level::Low}};
// CheckedTransformer translates and checks whether the results are either in one of the strings or in one of the
// translations already
app.add_option("-l,--level", level, "Level settings")
->required()
->transform(CLI::CheckedTransformer(map, CLI::ignore_case));
CLI11_PARSE(app, argc, argv);
// CLI11's built in enum streaming can be used outside CLI11 like this:
using CLI::enums::operator<<;
std::cout << "Enum received: " << level << '\n';
return 0;
}
+51
View File
@@ -0,0 +1,51 @@
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#define CLI11_ENABLE_EXTRA_VALIDATORS 1
#include <CLI/CLI.hpp>
#include <iostream>
#include <map>
#include <string>
enum class Level : int { High, Medium, Low };
// Defining operator<<() for your enum class (in this case for 'Level') overrides CLI11's enum streaming
inline std::ostream &operator<<(std::ostream &os, const Level &level) {
switch(level) {
case Level::High:
os << "High";
break;
case Level::Medium:
os << "Medium";
break;
case Level::Low:
os << "Low";
break;
}
os << " (ft rom custom ostream)";
return os;
}
int main(int argc, char **argv) {
CLI::App app;
Level level{Level::Low};
// specify string->value mappings
std::map<std::string, Level> map{{"high", Level::High}, {"medium", Level::Medium}, {"low", Level::Low}};
// CheckedTransformer translates and checks whether the results are either in one of the strings or in one of the
// translations already
app.add_option("-l,--level", level, "Level settings")
->required()
->transform(CLI::CheckedTransformer(map, CLI::ignore_case));
CLI11_PARSE(app, argc, argv);
// CLI11's built in enum streaming can be used outside CLI11 like this:
using CLI::enums::operator<<;
std::cout << "Enum received: " << level << '\n';
return 0;
}
+38
View File
@@ -0,0 +1,38 @@
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#include <CLI/CLI.hpp>
#include <iostream>
#include <memory>
#include <string>
class MyFormatter : public CLI::Formatter {
public:
MyFormatter() : Formatter() {}
std::string make_option_opts(const CLI::Option *) const override { return " OPTION"; }
};
int main(int argc, char **argv) {
CLI::App app;
app.set_help_all_flag("--help-all", "Show all help");
auto fmt = std::make_shared<MyFormatter>();
fmt->column_width(15);
app.formatter(fmt);
app.add_flag("--flag", "This is a flag");
auto *sub1 = app.add_subcommand("one", "Description One");
sub1->add_flag("--oneflag", "Some flag");
auto *sub2 = app.add_subcommand("two", "Description Two");
sub2->add_flag("--twoflag", "Some other flag");
CLI11_PARSE(app, argc, argv);
std::cout << "This app was meant to show off the formatter, run with -h" << '\n';
return 0;
}
+39
View File
@@ -0,0 +1,39 @@
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#include <CLI/CLI.hpp>
#include <CLI/Timer.hpp>
#include <iostream>
#include <string>
int main(int argc, char **argv) {
CLI::AutoTimer give_me_a_name("This is a timer");
CLI::App app("K3Pi goofit fitter");
std::string file;
CLI::Option *opt = app.add_option("-f,--file,file", file, "File name")->required()->group("Important");
int count{0};
CLI::Option *copt = app.add_flag("-c,--count", count, "Counter")->required()->group("Important");
double value{0.0}; // = 3.14;
app.add_option("-d,--double", value, "Some Value")->group("Other");
try {
app.parse(argc, argv);
} catch(const CLI::ParseError &e) {
return app.exit(e);
}
std::cout << "Working on file: " << file << ", direct count: " << app.count("--file")
<< ", opt count: " << opt->count() << '\n';
std::cout << "Working on count: " << count << ", direct count: " << app.count("--count")
<< ", opt count: " << copt->count() << '\n';
std::cout << "Some value: " << value << '\n';
return 0;
}
+76
View File
@@ -0,0 +1,76 @@
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#include <CLI/CLI.hpp>
#include <string>
int main(int argc, char *argv[]) {
std::string input_file_name, output_file_name;
int level{5}, subopt{0};
// app caption
CLI::App app{"CLI11 help"};
// this tests out some of the wide character support, mainly for compilation checking
argv = app.ensure_utf8(argv);
app.require_subcommand(1);
// subcommands options and flags
CLI::App *const encode = app.add_subcommand("e", "encode")->ignore_case(); // ignore case
encode->add_option("input", input_file_name, "input file")
->option_text(" ")
->required()
->check(CLI::ExistingFile); // file must exist
encode->add_option("output", output_file_name, "output file")->option_text(" ")->required(); // required option
encode->add_option("-l, --level", level, "encoding level")
->option_text("[1..9]")
->check(CLI::Range(1, 9))
->default_val(5); // limit parameter range
encode->add_flag("-R, --remove", "remove input file"); // no parameter option
encode->add_flag("-s, --suboption", subopt, "suboption")->option_text(" ");
CLI::App *const decode = app.add_subcommand("d", "decode")->ignore_case();
decode->add_option("input", input_file_name, "input file")->option_text(" ")->required()->check(CLI::ExistingFile);
decode->add_option("output", output_file_name, "output file")->option_text(" ")->required();
// Usage message modification
std::string usage_msg = "Usage: " + std::string(argv[0]) + " <command> [options] <input-file> <output-file>";
app.usage(usage_msg);
// flag to display full help at once
app.set_help_flag("");
app.set_help_all_flag("-h, --help");
CLI11_PARSE(app, argc, argv);
return 0;
}
/*
$ ./help_usage -h
CLI11 help
OPTIONS:
-h, --help
SUBCOMMANDS:
e
encode
POSITIONALS:
input input file
output output file
OPTIONS:
-l, --level [1..9] encoding level
-R, --remove remove input file
-s, --suboption suboption
d
decode
POSITIONALS:
input input file
output output file
*/
@@ -0,0 +1,52 @@
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#include <CLI/CLI.hpp>
#include <algorithm>
#include <iostream>
#include <string>
#include <tuple>
#include <vector>
int main(int argc, char **argv) {
CLI::App app{"An app to practice mixing unlimited arguments, but still recover the original order."};
std::vector<int> foos;
auto *foo = app.add_option("--foo,-f", foos, "Some unlimited argument");
std::vector<int> bars;
auto *bar = app.add_option("--bar", bars, "Some unlimited argument");
app.add_flag("--z,--x", "Random other flags");
// Standard parsing lines (copy and paste in, or use CLI11_PARSE)
try {
app.parse(argc, argv);
} catch(const CLI::ParseError &e) {
return app.exit(e);
}
// I prefer using the back and popping
std::reverse(std::begin(foos), std::end(foos));
std::reverse(std::begin(bars), std::end(bars));
std::vector<std::pair<std::string, int>> keyval;
for(auto *option : app.parse_order()) {
if(option == foo) {
keyval.emplace_back("foo", foos.back());
foos.pop_back();
}
if(option == bar) {
keyval.emplace_back("bar", bars.back());
bars.pop_back();
}
}
// Prove the vector is correct
for(auto &pair : keyval) {
std::cout << pair.first << " : " << pair.second << '\n';
}
}
+123
View File
@@ -0,0 +1,123 @@
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#include <CLI/CLI.hpp>
#include <iostream>
#include <memory>
#include <nlohmann/json.hpp>
#include <string>
#include <vector>
// This example is only built on GCC 7 on Travis due to mismatch in stdlib
// for clang (CLI11 is forgiving about mismatches, json.hpp is not)
using nlohmann::json;
class ConfigJSON : public CLI::Config {
public:
std::string to_config(const CLI::App *app, bool default_also, bool, std::string) const override {
json j;
for(const CLI::Option *opt : app->get_options({})) {
// Only process option with a long-name and configurable
if(!opt->get_lnames().empty() && opt->get_configurable()) {
std::string name = opt->get_lnames()[0];
// Non-flags
if(opt->get_type_size() != 0) {
// If the option was found on command line
if(opt->count() == 1)
j[name] = opt->results().at(0);
else if(opt->count() > 1)
j[name] = opt->results();
// If the option has a default and is requested by optional argument
else if(default_also && !opt->get_default_str().empty())
j[name] = opt->get_default_str();
// Flag, one passed
} else if(opt->count() == 1) {
j[name] = true;
// Flag, multiple passed
} else if(opt->count() > 1) {
j[name] = opt->count();
// Flag, not present
} else if(opt->count() == 0 && default_also) {
j[name] = false;
}
}
}
for(const CLI::App *subcom : app->get_subcommands({}))
j[subcom->get_name()] = json(to_config(subcom, default_also, false, ""));
return j.dump(4);
}
std::vector<CLI::ConfigItem> from_config(std::istream &input) const override {
json j;
input >> j;
return _from_config(j);
}
std::vector<CLI::ConfigItem>
_from_config(json j, std::string name = "", std::vector<std::string> prefix = {}) const {
std::vector<CLI::ConfigItem> results;
if(j.is_object()) {
for(json::iterator item = j.begin(); item != j.end(); ++item) {
auto copy_prefix = prefix;
if(!name.empty())
copy_prefix.push_back(name);
auto sub_results = _from_config(*item, item.key(), copy_prefix);
results.insert(results.end(), sub_results.begin(), sub_results.end());
}
} else if(!name.empty()) {
results.emplace_back();
CLI::ConfigItem &res = results.back();
res.name = name;
res.parents = prefix;
if(j.is_boolean()) {
res.inputs = {j.get<bool>() ? "true" : "false"};
} else if(j.is_number()) {
std::stringstream ss;
ss << j.get<double>();
res.inputs = {ss.str()};
} else if(j.is_string()) {
res.inputs = {j.get<std::string>()};
} else if(j.is_array()) {
for(std::string ival : j)
res.inputs.push_back(ival);
} else {
throw CLI::ConversionError("Failed to convert " + name);
}
} else {
throw CLI::ConversionError("You must make all top level values objects in json!");
}
return results;
}
};
int main(int argc, char **argv) {
CLI::App app;
app.config_formatter(std::make_shared<ConfigJSON>());
int item;
app.add_flag("--simple");
app.add_option("--item", item);
app.set_config("--config");
CLI11_PARSE(app, argc, argv);
std::cout << app.config_to_str(true, true) << std::endl;
}
+19
View File
@@ -0,0 +1,19 @@
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
// Code modified from https://github.com/CLIUtils/CLI11/issues/1334
#include <CLI/CLI.hpp>
/** This example is a minimal example of using CLI11. It does not do anything, but it compiles and runs, and can be used
* as a starting point for new projects.
*/
int main(int argc, char **argv) {
CLI::App app{"App description"};
CLI11_PARSE(app, argc, argv);
return 0;
}
+35
View File
@@ -0,0 +1,35 @@
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#include <CLI/CLI.hpp>
#include <iostream>
#include <string>
int main(int argc, char **argv) {
CLI::App test{R"raw(Modify the help print so that argument values are accessible.
Note that this will not shortcut `->required` and other similar options.)raw"};
// Remove help flag because it shortcuts all processing
test.set_help_flag();
// Add custom flag that activates help
auto *help = test.add_flag("-h,--help", "Request help");
std::string some_option;
test.add_option("-a", some_option, "Some description");
try {
test.parse(argc, argv);
if(*help)
throw CLI::CallForHelp();
} catch(const CLI::Error &e) {
std::cout << "Option -a string in help: " << some_option << '\n';
return test.exit(e);
}
std::cout << "Option -a string: " << some_option << '\n';
return 0;
}
+31
View File
@@ -0,0 +1,31 @@
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#define CLI11_ENABLE_EXTRA_VALIDATORS 1
#include <CLI/CLI.hpp>
#include <string>
int main(int argc, char **argv) {
CLI::App app("Vision Application");
app.set_help_all_flag("--help-all", "Expand all help");
app.add_flag("--version", "Get version");
CLI::App *cameraApp = app.add_subcommand("camera", "Configure the app camera");
cameraApp->require_subcommand(0, 1); // 0 (default) or 1 camera
std::string mvcamera_config_file = "mvcamera_config.json";
CLI::App *mvcameraApp = cameraApp->add_subcommand("mvcamera", "MatrixVision Camera Configuration");
mvcameraApp->add_option("-c,--config", mvcamera_config_file, "Config filename")
->capture_default_str()
->check(CLI::ExistingFile);
std::string mock_camera_path;
CLI::App *mockcameraApp = cameraApp->add_subcommand("mock", "Mock Camera Configuration");
mockcameraApp->add_option("-p,--path", mock_camera_path, "Path")->required()->check(CLI::ExistingPath);
CLI11_PARSE(app, argc, argv);
}
+46
View File
@@ -0,0 +1,46 @@
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#include <CLI/CLI.hpp>
#include <iostream>
#include <string>
int main(int argc, char **argv) {
CLI::App app("data output specification");
app.set_help_all_flag("--help-all", "Expand all help");
auto *format = app.add_option_group("output_format", "formatting type for output");
auto *target = app.add_option_group("output target", "target location for the output");
bool csv{false};
bool human{false};
bool binary{false};
format->add_flag("--csv", csv, "specify the output in csv format");
format->add_flag("--human", human, "specify the output in human readable text format");
format->add_flag("--binary", binary, "specify the output in binary format");
// require one of the options to be selected
format->require_option(1);
std::string fileLoc;
std::string networkAddress;
target->add_option("-o,--file", fileLoc, "specify the file location of the output");
target->add_option("--address", networkAddress, "specify a network address to send the file");
// require at most one of the target options
target->require_option(0, 1);
CLI11_PARSE(app, argc, argv);
std::string format_type = (csv) ? std::string("CSV") : ((human) ? "human readable" : "binary");
std::cout << "Selected " << format_type << " format\n";
if(!fileLoc.empty()) {
std::cout << " sent to file " << fileLoc << '\n';
} else if(!networkAddress.empty()) {
std::cout << " sent over network to " << networkAddress << '\n';
} else {
std::cout << " sent to std::cout\n";
}
return 0;
}
+46
View File
@@ -0,0 +1,46 @@
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#include <CLI/CLI.hpp>
#include <iostream>
#include <string>
int main(int argc, char **argv) {
CLI::App app("test for positional arity");
auto *numbers = app.add_option_group("numbers", "specify key numbers");
auto *files = app.add_option_group("files", "specify files");
int num1{-1}, num2{-1};
numbers->add_option("num1", num1, "first number");
numbers->add_option("num2", num2, "second number");
std::string file1, file2;
files->add_option("file1", file1, "first file")->required();
files->add_option("file2", file2, "second file");
// set a pre parse callback that turns the numbers group on or off depending on the number of arguments
app.preparse_callback([numbers](std::size_t arity) {
if(arity <= 2) {
numbers->disabled();
} else {
numbers->disabled(false);
}
});
CLI11_PARSE(app, argc, argv);
if(num1 != -1)
std::cout << "Num1 = " << num1 << '\n';
if(num2 != -1)
std::cout << "Num2 = " << num2 << '\n';
std::cout << "File 1 = " << file1 << '\n';
if(!file2.empty()) {
std::cout << "File 2 = " << file2 << '\n';
}
return 0;
}
@@ -0,0 +1,39 @@
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#define CLI11_ENABLE_EXTRA_VALIDATORS 1
#include <CLI/CLI.hpp>
#include <iostream>
#include <string>
int main(int argc, char **argv) {
CLI::App app("test for positional validation");
int num1{-1}, num2{-1};
app.add_option("num1", num1, "first number")->check(CLI::Number);
app.add_option("num2", num2, "second number")->check(CLI::Number);
std::string file1, file2;
app.add_option("file1", file1, "first file")->required();
app.add_option("file2", file2, "second file");
app.validate_positionals();
CLI11_PARSE(app, argc, argv);
if(num1 != -1)
std::cout << "Num1 = " << num1 << '\n';
if(num2 != -1)
std::cout << "Num2 = " << num2 << '\n';
std::cout << "File 1 = " << file1 << '\n';
if(!file2.empty()) {
std::cout << "File 2 = " << file2 << '\n';
}
return 0;
}
+35
View File
@@ -0,0 +1,35 @@
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#include <CLI/CLI.hpp>
#include <iostream>
#include <string>
#include <vector>
int main(int argc, char **argv) {
CLI::App app("Prefix command app");
app.prefix_command(CLI::PrefixCommandMode::On);
std::vector<int> vals;
app.add_option("--vals,-v", vals)->expected(-1);
CLI11_PARSE(app, argc, argv);
std::vector<std::string> more_comms = app.remaining();
std::cout << "Prefix";
for(int v : vals)
std::cout << ": " << v << " ";
std::cout << '\n' << "Remaining commands: ";
for(const auto &com : more_comms)
std::cout << com << " ";
std::cout << '\n';
return 0;
}
+41
View File
@@ -0,0 +1,41 @@
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#include <CLI/CLI.hpp>
#include <iostream>
#include <vector>
int main(int argc, char **argv) {
CLI::App app{"App to demonstrate exclusionary option groups."};
std::vector<int> range;
app.add_option("--range,-R", range, "A range")->expected(-2);
auto *ogroup = app.add_option_group("min_max_step", "set the min max and step");
int min{0}, max{0}, step{1};
ogroup->add_option("--min,-m", min, "The minimum")->required();
ogroup->add_option("--max,-M", max, "The maximum")->required();
ogroup->add_option("--step,-s", step, "The step")->capture_default_str();
app.require_option(1);
CLI11_PARSE(app, argc, argv);
if(!range.empty()) {
if(range.size() == 2) {
min = range[0];
max = range[1];
}
if(range.size() >= 3) {
step = range[0];
min = range[1];
max = range[2];
}
}
std::cout << "range is [" << min << ':' << step << ':' << max << "]\n";
return 0;
}
+46
View File
@@ -0,0 +1,46 @@
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#include <CLI/CLI.hpp>
#include <iostream>
#include <utility>
#include <vector>
// This example shows the usage of the retired and deprecated option helper methods
int main(int argc, char **argv) {
CLI::App app("example for retired/deprecated options");
std::vector<int> x;
auto *opt1 = app.add_option("--retired_option2", x);
std::pair<int, int> y;
auto *opt2 = app.add_option("--deprecate", y);
app.add_option("--not_deprecated", x);
// specify that a non-existing option is retired
CLI::retire_option(app, "--retired_option");
// specify that an existing option is retired and non-functional: this will replace the option with another that
// behaves the same but does nothing
CLI::retire_option(app, opt1);
// deprecate an existing option and specify the recommended replacement
CLI::deprecate_option(opt2, "--not_deprecated");
CLI11_PARSE(app, argc, argv);
if(!x.empty()) {
std::cout << "Retired option example: got --not_deprecated values:";
for(auto &xval : x) {
std::cout << xval << " ";
}
std::cout << '\n';
} else if(app.count_all() == 1) {
std::cout << "Retired option example: no arguments received\n";
}
return 0;
}
+56
View File
@@ -0,0 +1,56 @@
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#include <CLI/CLI.hpp>
#include <iostream>
#include <vector>
int main(int argc, char **argv) {
CLI::App app("load shapes");
app.set_help_all_flag("--help-all");
auto *circle = app.add_subcommand("circle", "draw a circle")->immediate_callback();
double radius{0.0};
int circle_counter{0};
circle->callback([&radius, &circle_counter] {
++circle_counter;
std::cout << "circle" << circle_counter << " with radius " << radius << '\n';
});
circle->add_option("radius", radius, "the radius of the circle")->required();
auto *rect = app.add_subcommand("rectangle", "draw a rectangle")->immediate_callback();
double edge1{0.0};
double edge2{0.0};
int rect_counter{0};
rect->callback([&edge1, &edge2, &rect_counter] {
++rect_counter;
if(edge2 == 0) {
edge2 = edge1;
}
std::cout << "rectangle" << rect_counter << " with edges [" << edge1 << ',' << edge2 << "]\n";
edge2 = 0;
});
rect->add_option("edge1", edge1, "the first edge length of the rectangle")->required();
rect->add_option("edge2", edge2, "the second edge length of the rectangle");
auto *tri = app.add_subcommand("triangle", "draw a rectangle")->immediate_callback();
std::vector<double> sides;
int tri_counter = 0;
tri->callback([&sides, &tri_counter] {
++tri_counter;
std::cout << "triangle" << tri_counter << " with sides [" << CLI::detail::join(sides) << "]\n";
});
tri->add_option("sides", sides, "the side lengths of the triangle");
CLI11_PARSE(app, argc, argv);
return 0;
}
+38
View File
@@ -0,0 +1,38 @@
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#include <CLI/CLI.hpp>
#include <iostream>
#include <string>
int main(int argc, char **argv) {
CLI::App app("K3Pi goofit fitter");
// add version output
app.set_version_flag("--version", std::string(CLI11_VERSION));
std::string file;
CLI::Option *opt = app.add_option("-f,--file,file", file, "File name");
int count{0};
CLI::Option *copt = app.add_option("-c,--count", count, "Counter");
int v{0};
CLI::Option *flag = app.add_flag("--flag", v, "Some flag that can be passed multiple times");
double value{0.0}; // = 3.14;
app.add_option("-d,--double", value, "Some Value");
CLI11_PARSE(app, argc, argv);
std::cout << "Working on file: " << file << ", direct count: " << app.count("--file")
<< ", opt count: " << opt->count() << '\n';
std::cout << "Working on count: " << count << ", direct count: " << app.count("--count")
<< ", opt count: " << copt->count() << '\n';
std::cout << "Received flag: " << v << " (" << flag->count() << ") times\n";
std::cout << "Some value: " << value << '\n';
return 0;
}
+21
View File
@@ -0,0 +1,21 @@
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#include <CLI/CLI.hpp>
#include <iostream>
#include <string>
int main(int argc, char *argv[]) {
CLI::App cli_global{"Demo app"};
auto &cli_sub = *cli_global.add_subcommand("sub", "Some subcommand");
std::string sub_arg;
cli_sub.add_option("sub_arg", sub_arg, "Argument for subcommand")->required();
CLI11_PARSE(cli_global, argc, argv);
if(cli_sub) {
std::cout << "Got: " << sub_arg << '\n';
}
return 0;
}
@@ -0,0 +1 @@
add_cli_exe(subcommand_main subcommand_main.cpp subcommand_a.cpp subcommand_a.hpp)
@@ -0,0 +1,37 @@
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#include "subcommand_a.hpp"
#include <iostream>
#include <memory>
/// Set up a subcommand and capture a shared_ptr to a struct that holds all its options.
/// The variables of the struct are bound to the CLI options.
/// We use a shared ptr so that the addresses of the variables remain for binding,
/// You could return the shared pointer if you wanted to access the values in main.
void setup_subcommand_a(CLI::App &app) {
// Create the option and subcommand objects.
auto opt = std::make_shared<SubcommandAOptions>();
auto *sub = app.add_subcommand("subcommand_a", "performs subcommand a");
// Add options to sub, binding them to opt.
sub->add_option("-f,--file", opt->file, "File name")->required();
sub->add_flag("--with-foo", opt->with_foo, "Counter");
// Set the run function as callback to be called when this subcommand is issued.
sub->callback([opt]() { run_subcommand_a(*opt); });
}
/// The function that runs our code.
/// This could also simply be in the callback lambda itself,
/// but having a separate function is cleaner.
void run_subcommand_a(SubcommandAOptions const &opt) {
// Do stuff...
std::cout << "Working on file: " << opt.file << '\n';
if(opt.with_foo) {
std::cout << "Using foo!" << '\n';
}
}
@@ -0,0 +1,23 @@
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#pragma once
#include <CLI/CLI.hpp>
#include <string>
/// Collection of all options of Subcommand A.
struct SubcommandAOptions {
std::string file;
bool with_foo;
};
// We could manually make a few variables and use shared pointers for each; this
// is just done this way to be nicely organized
// Function declarations.
void setup_subcommand_a(CLI::App &app);
void run_subcommand_a(SubcommandAOptions const &opt);
@@ -0,0 +1,26 @@
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#include "subcommand_a.hpp"
#include <CLI/CLI.hpp>
int main(int argc, char **argv) {
CLI::App app{"..."};
// Call the setup functions for the subcommands.
// They are kept alive by a shared pointer in the
// lambda function held by CLI11
setup_subcommand_a(app);
// Make sure we get at least one subcommand
app.require_subcommand();
// More setup if needed, i.e., other subcommands etc.
CLI11_PARSE(app, argc, argv);
return 0;
}
+46
View File
@@ -0,0 +1,46 @@
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#include <CLI/CLI.hpp>
#include <CLI/Timer.hpp>
#include <iostream>
#include <memory>
#include <string>
int main(int argc, char **argv) {
CLI::AutoTimer give_me_a_name("This is a timer");
CLI::App app("K3Pi goofit fitter");
CLI::App_p impOpt = std::make_shared<CLI::App>("Important");
std::string file;
CLI::Option *opt = impOpt->add_option("-f,--file,file", file, "File name")->required();
int count{0};
CLI::Option *copt = impOpt->add_flag("-c,--count", count, "Counter")->required();
CLI::App_p otherOpt = std::make_shared<CLI::App>("Other");
double value{0.0}; // = 3.14;
otherOpt->add_option("-d,--double", value, "Some Value");
// add the subapps to the main one
app.add_subcommand(impOpt);
app.add_subcommand(otherOpt);
try {
app.parse(argc, argv);
} catch(const CLI::ParseError &e) {
return app.exit(e);
}
std::cout << "Working on file: " << file << ", direct count: " << impOpt->count("--file")
<< ", opt count: " << opt->count() << '\n';
std::cout << "Working on count: " << count << ", direct count: " << impOpt->count("--count")
<< ", opt count: " << copt->count() << '\n';
std::cout << "Some value: " << value << '\n';
return 0;
}
+34
View File
@@ -0,0 +1,34 @@
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#include <CLI/CLI.hpp>
#include <iostream>
#include <string>
int main(int argc, char **argv) {
CLI::App app("K3Pi goofit fitter");
app.set_help_all_flag("--help-all", "Expand all help");
app.add_flag("--random", "Some random flag");
CLI::App *start = app.add_subcommand("start", "A great subcommand");
CLI::App *stop = app.add_subcommand("stop", "Do you really want to stop?");
app.require_subcommand(); // 1 or more
std::string file;
start->add_option("-f,--file", file, "File name");
CLI::Option *s = stop->add_flag("-c,--count", "Counter");
CLI11_PARSE(app, argc, argv);
std::cout << "Working on --file from start: " << file << '\n';
std::cout << "Working on --count from stop: " << s->count() << ", direct count: " << stop->count("--count") << '\n';
std::cout << "Count of --random flag: " << app.count("--random") << '\n';
for(auto *subcom : app.get_subcommands())
std::cout << "Subcommand: " << subcom->get_name() << '\n';
return 0;
}
+28
View File
@@ -0,0 +1,28 @@
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
// Code modified from https://github.com/CLIUtils/CLI11/issues/559
#include <CLI/CLI.hpp>
#include <iostream>
#include <string>
int main(int argc, const char *argv[]) {
int value{0};
CLI::App app{"Test App"};
app.add_option("-v", value, "value");
auto *subcom = app.add_subcommand("sub", "")->prefix_command();
CLI11_PARSE(app, argc, argv);
std::cout << "value =" << value << '\n';
std::cout << "after Args:";
for(const auto &aarg : subcom->remaining()) {
std::cout << aarg << " ";
}
std::cout << '\n';
}
+25
View File
@@ -0,0 +1,25 @@
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#include <CLI/CLI.hpp>
#include <iostream>
#include <string>
int main(int argc, char **argv) {
CLI::App app("Validator checker");
std::string file;
app.add_option("-f,--file,file", file, "File name")->check(CLI::ExistingFile);
int count{0};
app.add_option("-v,--value", count, "Value in range")->check(CLI::Range(3, 6));
CLI11_PARSE(app, argc, argv);
std::cout << "Try printing help or failing the validator" << '\n';
return 0;
}