mirror of
https://github.com/CLIUtils/CLI11.git
synced 2025-01-15 14:48:00 +00:00
Compare commits
2 Commits
e2e3cb2fed
...
ee2b725019
Author | SHA1 | Date | |
---|---|---|---|
|
ee2b725019 | ||
|
c4f1fc8ea7 |
@ -23,6 +23,7 @@
|
||||
* The final "defaulted" bool has been removed, use `->capture_default_str()`
|
||||
instead. Use `app.option_defaults()->always_capture_default()` to set this for
|
||||
all future options. [#597][]
|
||||
* Use `add_option` on a complex number instead of `add_complex`, which has been removed.
|
||||
|
||||
|
||||
[#435]: https://github.com/CLIUtils/CLI11/pull/435
|
||||
|
@ -231,8 +231,6 @@ app.add_option_function<type>(option_name,
|
||||
function <void(const type &value)>, // type can be any type supported by add_option
|
||||
help_string="")
|
||||
|
||||
app.add_complex(... // Special case: support for complex numbers ⚠️. Complex numbers are now fully supported in the add_option so this function is redundant.
|
||||
|
||||
// char as an option type is supported before 2.0 but in 2.0 it defaulted to allowing single non numerical characters in addition to the numeric values.
|
||||
|
||||
// 🆕 There is a template overload which takes two template parameters the first is the type of object to assign the value to, the second is the conversion type. The conversion type should have a known way to convert from a string, such as any of the types that work in the non-template version. If XC is a std::pair and T is some non pair type. Then a two argument constructor for T is called to assign the value. For tuples or other multi element types, XC must be a single type or a tuple like object of the same size as the assignment type
|
||||
@ -758,7 +756,7 @@ If it is desired that multiple configuration be allowed. Use
|
||||
app.set_config("--config")->expected(1, X);
|
||||
```
|
||||
|
||||
Where X is some positive number and will allow up to `X` configuration files to be specified by separate `--config` arguments.
|
||||
Where X is some positive number and will allow up to `X` configuration files to be specified by separate `--config` arguments. Value strings with quote characters in it will be printed with a single quote.🚧 All other arguments will use double quote. Empty strings will use a double quoted argument.🚧 Numerical or boolean values are not quoted. 🚧
|
||||
|
||||
### Inheriting defaults
|
||||
|
||||
|
@ -81,6 +81,22 @@ add_test(NAME subcom_partitioned_help COMMAND subcom_partitioned --help)
|
||||
set_property(TEST subcom_partitioned_help PROPERTY PASS_REGULAR_EXPRESSION
|
||||
"-f,--file TEXT REQUIRED" "-d,--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"
|
||||
|
50
examples/config_app.cpp
Normal file
50
examples/config_app.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
// Copyright (c) 2017-2020, 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() << std::endl;
|
||||
std::cout << "Working on count: " << count << ", direct count: " << app.count("--count")
|
||||
<< ", opt count: " << copt->count() << std::endl;
|
||||
std::cout << "Received flag: " << v << " (" << flag->count() << ") times\n";
|
||||
std::cout << "Some value: " << value << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
@ -896,56 +896,6 @@ class App {
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Add a complex number DEPRECATED --use add_option instead
|
||||
template <typename T, typename XC = double>
|
||||
Option *add_complex(std::string option_name,
|
||||
T &variable,
|
||||
std::string option_description = "",
|
||||
bool defaulted = false,
|
||||
std::string label = "COMPLEX") {
|
||||
|
||||
CLI::callback_t fun = [&variable](const results_t &res) {
|
||||
XC x, y;
|
||||
bool worked;
|
||||
if(res.size() >= 2 && !res[1].empty()) {
|
||||
auto str1 = res[1];
|
||||
if(str1.back() == 'i' || str1.back() == 'j')
|
||||
str1.pop_back();
|
||||
worked = detail::lexical_cast(res[0], x) && detail::lexical_cast(str1, y);
|
||||
} else {
|
||||
auto str1 = res.front();
|
||||
auto nloc = str1.find_last_of('-');
|
||||
if(nloc != std::string::npos && nloc > 0) {
|
||||
worked = detail::lexical_cast(str1.substr(0, nloc), x);
|
||||
str1 = str1.substr(nloc);
|
||||
if(str1.back() == 'i' || str1.back() == 'j')
|
||||
str1.pop_back();
|
||||
worked = worked && detail::lexical_cast(str1, y);
|
||||
} else {
|
||||
if(str1.back() == 'i' || str1.back() == 'j') {
|
||||
str1.pop_back();
|
||||
worked = detail::lexical_cast(str1, y);
|
||||
x = XC{0};
|
||||
} else {
|
||||
worked = detail::lexical_cast(str1, x);
|
||||
y = XC{0};
|
||||
}
|
||||
}
|
||||
}
|
||||
if(worked)
|
||||
variable = T{x, y};
|
||||
return worked;
|
||||
};
|
||||
|
||||
auto default_function = [&variable]() { return CLI::detail::checked_to_string<T, T>(variable); };
|
||||
|
||||
CLI::Option *opt =
|
||||
add_option(option_name, std::move(fun), std::move(option_description), defaulted, default_function);
|
||||
|
||||
opt->type_name(label)->type_size(1, 2)->delimiter('+')->run_callback_for_default();
|
||||
return opt;
|
||||
}
|
||||
|
||||
/// Set a configuration ini file option, or clear it if no name passed
|
||||
Option *set_config(std::string option_name = "",
|
||||
std::string default_filename = "",
|
||||
|
@ -23,9 +23,9 @@ namespace CLI {
|
||||
// [CLI11:config_hpp:verbatim]
|
||||
namespace detail {
|
||||
|
||||
inline std::string convert_arg_for_ini(const std::string &arg) {
|
||||
inline std::string convert_arg_for_ini(const std::string &arg, char stringQuote = '"', char characterQuote = '\'') {
|
||||
if(arg.empty()) {
|
||||
return std::string(2, '"');
|
||||
return std::string(2, stringQuote);
|
||||
}
|
||||
// some specifically supported strings
|
||||
if(arg == "true" || arg == "false" || arg == "nan" || arg == "inf") {
|
||||
@ -40,7 +40,7 @@ inline std::string convert_arg_for_ini(const std::string &arg) {
|
||||
}
|
||||
// just quote a single non numeric character
|
||||
if(arg.size() == 1) {
|
||||
return std::string("'") + arg + '\'';
|
||||
return std::string(1, characterQuote) + arg + characterQuote;
|
||||
}
|
||||
// handle hex, binary or octal arguments
|
||||
if(arg.front() == '0') {
|
||||
@ -60,16 +60,20 @@ inline std::string convert_arg_for_ini(const std::string &arg) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if(arg.find_first_of('"') == std::string::npos) {
|
||||
return std::string("\"") + arg + '"';
|
||||
if(arg.find_first_of(stringQuote) == std::string::npos) {
|
||||
return std::string(1, stringQuote) + arg + stringQuote;
|
||||
} else {
|
||||
return std::string("'") + arg + '\'';
|
||||
return characterQuote + arg + characterQuote;
|
||||
}
|
||||
}
|
||||
|
||||
/// Comma separated join, adds quotes if needed
|
||||
inline std::string
|
||||
ini_join(const std::vector<std::string> &args, char sepChar = ',', char arrayStart = '[', char arrayEnd = ']') {
|
||||
inline std::string ini_join(const std::vector<std::string> &args,
|
||||
char sepChar = ',',
|
||||
char arrayStart = '[',
|
||||
char arrayEnd = ']',
|
||||
char stringQuote = '"',
|
||||
char characterQuote = '\'') {
|
||||
std::string joined;
|
||||
if(args.size() > 1 && arrayStart != '\0') {
|
||||
joined.push_back(arrayStart);
|
||||
@ -82,7 +86,7 @@ ini_join(const std::vector<std::string> &args, char sepChar = ',', char arraySta
|
||||
joined.push_back(' ');
|
||||
}
|
||||
}
|
||||
joined.append(convert_arg_for_ini(arg));
|
||||
joined.append(convert_arg_for_ini(arg, stringQuote, characterQuote));
|
||||
}
|
||||
if(args.size() > 1 && arrayEnd != '\0') {
|
||||
joined.push_back(arrayEnd);
|
||||
@ -296,13 +300,16 @@ ConfigBase::to_config(const App *app, bool default_also, bool write_description,
|
||||
}
|
||||
}
|
||||
std::string name = prefix + opt->get_single_name();
|
||||
std::string value = detail::ini_join(opt->reduced_results(), arraySeparator, arrayStart, arrayEnd);
|
||||
std::string value = detail::ini_join(
|
||||
opt->reduced_results(), arraySeparator, arrayStart, arrayEnd, stringQuote, characterQuote);
|
||||
|
||||
if(value.empty() && default_also) {
|
||||
if(!opt->get_default_str().empty()) {
|
||||
value = detail::convert_arg_for_ini(opt->get_default_str());
|
||||
value = detail::convert_arg_for_ini(opt->get_default_str(), stringQuote, characterQuote);
|
||||
} else if(opt->get_expected_min() == 0) {
|
||||
value = "false";
|
||||
} else if(opt->get_run_callback_for_default()) {
|
||||
value = "\"\""; // empty string default value
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -87,6 +87,10 @@ class ConfigBase : public Config {
|
||||
char arraySeparator = ',';
|
||||
/// the character used separate the name from the value
|
||||
char valueDelimiter = '=';
|
||||
/// the character to use around strings
|
||||
char stringQuote = '"';
|
||||
/// the character to use around single characters
|
||||
char characterQuote = '\'';
|
||||
|
||||
public:
|
||||
std::string
|
||||
@ -114,6 +118,12 @@ class ConfigBase : public Config {
|
||||
valueDelimiter = vSep;
|
||||
return this;
|
||||
}
|
||||
/// Specify the quote characters used around strings and characters
|
||||
ConfigBase *quoteCharacter(char qString, char qChar) {
|
||||
stringQuote = qString;
|
||||
characterQuote = qChar;
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
/// the default Config is the TOML file format
|
||||
|
@ -809,7 +809,7 @@ TEST_CASE_METHOD(TApp, "TakeFirstOptMulti", "[app]") {
|
||||
|
||||
TEST_CASE_METHOD(TApp, "ComplexOptMulti", "[app]") {
|
||||
std::complex<double> val;
|
||||
app.add_complex("--long", val)->take_first()->allow_extra_args();
|
||||
app.add_option("--long", val)->take_first()->allow_extra_args();
|
||||
|
||||
args = {"--long", "1", "2", "3", "4"};
|
||||
|
||||
|
@ -13,26 +13,6 @@ using Catch::Matchers::Contains;
|
||||
|
||||
using cx = std::complex<double>;
|
||||
|
||||
TEST_CASE_METHOD(TApp, "Complex", "[newparse]") {
|
||||
cx comp{1, 2};
|
||||
app.add_complex("-c,--complex", comp, "", true);
|
||||
|
||||
args = {"-c", "4", "3"};
|
||||
|
||||
std::string help = app.help();
|
||||
CHECK_THAT(help, Contains("1"));
|
||||
CHECK_THAT(help, Contains("2"));
|
||||
CHECK_THAT(help, Contains("COMPLEX"));
|
||||
|
||||
CHECK(comp.real() == Approx(1));
|
||||
CHECK(comp.imag() == Approx(2));
|
||||
|
||||
run();
|
||||
|
||||
CHECK(comp.real() == Approx(4));
|
||||
CHECK(comp.imag() == Approx(3));
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(TApp, "ComplexOption", "[newparse]") {
|
||||
cx comp{1, 2};
|
||||
app.add_option("-c,--complex", comp)->capture_default_str();
|
||||
@ -53,26 +33,6 @@ TEST_CASE_METHOD(TApp, "ComplexOption", "[newparse]") {
|
||||
CHECK(comp.imag() == Approx(3));
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(TApp, "ComplexFloat", "[newparse]") {
|
||||
std::complex<float> comp{1, 2};
|
||||
app.add_complex<std::complex<float>, float>("-c,--complex", comp)->capture_default_str();
|
||||
|
||||
args = {"-c", "4", "3"};
|
||||
|
||||
std::string help = app.help();
|
||||
CHECK_THAT(help, Contains("1"));
|
||||
CHECK_THAT(help, Contains("2"));
|
||||
CHECK_THAT(help, Contains("COMPLEX"));
|
||||
|
||||
CHECK(comp.real() == Approx(1));
|
||||
CHECK(comp.imag() == Approx(2));
|
||||
|
||||
run();
|
||||
|
||||
CHECK(comp.real() == Approx(4));
|
||||
CHECK(comp.imag() == Approx(3));
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(TApp, "ComplexFloatOption", "[newparse]") {
|
||||
std::complex<float> comp{1, 2};
|
||||
app.add_option("-c,--complex", comp)->capture_default_str();
|
||||
@ -93,38 +53,6 @@ TEST_CASE_METHOD(TApp, "ComplexFloatOption", "[newparse]") {
|
||||
CHECK(comp.imag() == Approx(3));
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(TApp, "ComplexWithDelimiter", "[newparse]") {
|
||||
cx comp{1, 2};
|
||||
app.add_complex("-c,--complex", comp)->capture_default_str()->delimiter('+');
|
||||
|
||||
args = {"-c", "4+3i"};
|
||||
|
||||
std::string help = app.help();
|
||||
CHECK_THAT(help, Contains("1"));
|
||||
CHECK_THAT(help, Contains("2"));
|
||||
CHECK_THAT(help, Contains("COMPLEX"));
|
||||
|
||||
CHECK(comp.real() == Approx(1));
|
||||
CHECK(comp.imag() == Approx(2));
|
||||
|
||||
run();
|
||||
|
||||
CHECK(comp.real() == Approx(4));
|
||||
CHECK(comp.imag() == Approx(3));
|
||||
|
||||
args = {"-c", "5+-3i"};
|
||||
run();
|
||||
|
||||
CHECK(comp.real() == Approx(5));
|
||||
CHECK(comp.imag() == Approx(-3));
|
||||
|
||||
args = {"-c", "6", "-4i"};
|
||||
run();
|
||||
|
||||
CHECK(comp.real() == Approx(6));
|
||||
CHECK(comp.imag() == Approx(-4));
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(TApp, "ComplexWithDelimiterOption", "[newparse]") {
|
||||
cx comp{1, 2};
|
||||
app.add_option("-c,--complex", comp)->capture_default_str()->delimiter('+');
|
||||
@ -157,18 +85,6 @@ TEST_CASE_METHOD(TApp, "ComplexWithDelimiterOption", "[newparse]") {
|
||||
CHECK(comp.imag() == Approx(-4));
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(TApp, "ComplexIgnoreI", "[newparse]") {
|
||||
cx comp{1, 2};
|
||||
app.add_complex("-c,--complex", comp);
|
||||
|
||||
args = {"-c", "4", "3i"};
|
||||
|
||||
run();
|
||||
|
||||
CHECK(comp.real() == Approx(4));
|
||||
CHECK(comp.imag() == Approx(3));
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(TApp, "ComplexIgnoreIOption", "[newparse]") {
|
||||
cx comp{1, 2};
|
||||
app.add_option("-c,--complex", comp);
|
||||
@ -181,40 +97,6 @@ TEST_CASE_METHOD(TApp, "ComplexIgnoreIOption", "[newparse]") {
|
||||
CHECK(comp.imag() == Approx(3));
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(TApp, "ComplexSingleArg", "[newparse]") {
|
||||
cx comp{1, 2};
|
||||
app.add_complex("-c,--complex", comp);
|
||||
|
||||
args = {"-c", "4"};
|
||||
run();
|
||||
CHECK(comp.real() == Approx(4));
|
||||
CHECK(comp.imag() == Approx(0));
|
||||
|
||||
args = {"-c", "4-2i"};
|
||||
run();
|
||||
CHECK(comp.real() == Approx(4));
|
||||
CHECK(comp.imag() == Approx(-2));
|
||||
args = {"-c", "4+2i"};
|
||||
run();
|
||||
CHECK(comp.real() == Approx(4));
|
||||
CHECK(comp.imag() == Approx(2));
|
||||
|
||||
args = {"-c", "-4+2j"};
|
||||
run();
|
||||
CHECK(comp.real() == Approx(-4));
|
||||
CHECK(comp.imag() == Approx(2));
|
||||
|
||||
args = {"-c", "-4.2-2j"};
|
||||
run();
|
||||
CHECK(comp.real() == Approx(-4.2));
|
||||
CHECK(comp.imag() == Approx(-2));
|
||||
|
||||
args = {"-c", "-4.2-2.7i"};
|
||||
run();
|
||||
CHECK(comp.real() == Approx(-4.2));
|
||||
CHECK(comp.imag() == Approx(-2.7));
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(TApp, "ComplexSingleArgOption", "[newparse]") {
|
||||
cx comp{1, 2};
|
||||
app.add_option("-c,--complex", comp);
|
||||
@ -249,29 +131,6 @@ TEST_CASE_METHOD(TApp, "ComplexSingleArgOption", "[newparse]") {
|
||||
CHECK(comp.imag() == Approx(-2.7));
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(TApp, "ComplexSingleImag", "[newparse]") {
|
||||
cx comp{1, 2};
|
||||
app.add_complex("-c,--complex", comp);
|
||||
|
||||
args = {"-c", "4j"};
|
||||
run();
|
||||
CHECK(comp.real() == Approx(0));
|
||||
CHECK(comp.imag() == Approx(4));
|
||||
|
||||
args = {"-c", "-4j"};
|
||||
run();
|
||||
CHECK(comp.real() == Approx(0));
|
||||
CHECK(comp.imag() == Approx(-4));
|
||||
args = {"-c", "-4"};
|
||||
run();
|
||||
CHECK(comp.real() == Approx(-4));
|
||||
CHECK(comp.imag() == Approx(0));
|
||||
args = {"-c", "+4"};
|
||||
run();
|
||||
CHECK(comp.real() == Approx(4));
|
||||
CHECK(comp.imag() == Approx(0));
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(TApp, "ComplexSingleImagOption", "[newparse]") {
|
||||
cx comp{1, 2};
|
||||
app.add_option("-c,--complex", comp);
|
||||
|
Loading…
Reference in New Issue
Block a user