lib/thirdparty: update CLI11 to 2.3.2

This commit is contained in:
Dirk-Jan C. Binnema
2023-03-03 18:46:48 +02:00
parent 9d0fe0e5d4
commit 6f0e295326

View File

@ -1,11 +1,11 @@
// CLI11: Version 2.3.1 // CLI11: Version 2.3.2
// Originally designed by Henry Schreiner // Originally designed by Henry Schreiner
// https://github.com/CLIUtils/CLI11 // https://github.com/CLIUtils/CLI11
// //
// This is a standalone header file generated by MakeSingleHeader.py in CLI11/scripts // This is a standalone header file generated by MakeSingleHeader.py in CLI11/scripts
// from: v2.3.1 // from: v2.3.2
// //
// CLI11 2.3.1 Copyright (c) 2017-2022 University of Cincinnati, developed by Henry // CLI11 2.3.2 Copyright (c) 2017-2022 University of Cincinnati, developed by Henry
// Schreiner under NSF AWARD 1414736. All rights reserved. // Schreiner under NSF AWARD 1414736. All rights reserved.
// //
// Redistribution and use in source and binary forms of CLI11, with or without // Redistribution and use in source and binary forms of CLI11, with or without
@ -34,34 +34,34 @@
#pragma once #pragma once
// Standard combined includes: // Standard combined includes:
#include <functional>
#include <locale>
#include <cstdint>
#include <iomanip> #include <iomanip>
#include <memory>
#include <exception>
#include <stdexcept>
#include <string>
#include <vector>
#include <iterator>
#include <algorithm>
#include <sstream>
#include <cmath>
#include <type_traits>
#include <limits>
#include <set> #include <set>
#include <memory>
#include <vector>
#include <utility> #include <utility>
#include <map> #include <stdexcept>
#include <locale>
#include <functional>
#include <iostream> #include <iostream>
#include <iterator>
#include <exception>
#include <numeric> #include <numeric>
#include <fstream> #include <fstream>
#include <string>
#include <type_traits>
#include <tuple> #include <tuple>
#include <map>
#include <algorithm>
#include <cstdint>
#include <sstream>
#include <cmath>
#include <limits>
#define CLI11_VERSION_MAJOR 2 #define CLI11_VERSION_MAJOR 2
#define CLI11_VERSION_MINOR 3 #define CLI11_VERSION_MINOR 3
#define CLI11_VERSION_PATCH 1 #define CLI11_VERSION_PATCH 2
#define CLI11_VERSION "2.3.1" #define CLI11_VERSION "2.3.2"
@ -972,7 +972,9 @@ constexpr enabler dummy = {};
template <bool B, class T = void> using enable_if_t = typename std::enable_if<B, T>::type; template <bool B, class T = void> using enable_if_t = typename std::enable_if<B, T>::type;
/// A copy of std::void_t from C++17 (helper for C++11 and C++14) /// A copy of std::void_t from C++17 (helper for C++11 and C++14)
template <typename... Ts> struct make_void { using type = void; }; template <typename... Ts> struct make_void {
using type = void;
};
/// A copy of std::void_t from C++17 - same reasoning as enable_if_t, it does not hurt to redefine /// A copy of std::void_t from C++17 - same reasoning as enable_if_t, it does not hurt to redefine
template <typename... Ts> using void_t = typename make_void<Ts...>::type; template <typename... Ts> using void_t = typename make_void<Ts...>::type;
@ -1001,10 +1003,14 @@ template <typename T> struct is_copyable_ptr {
}; };
/// This can be specialized to override the type deduction for IsMember. /// This can be specialized to override the type deduction for IsMember.
template <typename T> struct IsMemberType { using type = T; }; template <typename T> struct IsMemberType {
using type = T;
};
/// The main custom type needed here is const char * should be a string. /// The main custom type needed here is const char * should be a string.
template <> struct IsMemberType<const char *> { using type = std::string; }; template <> struct IsMemberType<const char *> {
using type = std::string;
};
namespace detail { namespace detail {
@ -1014,7 +1020,9 @@ namespace detail {
/// pointer_traits<T> be valid. /// pointer_traits<T> be valid.
/// not a pointer /// not a pointer
template <typename T, typename Enable = void> struct element_type { using type = T; }; template <typename T, typename Enable = void> struct element_type {
using type = T;
};
template <typename T> struct element_type<T, typename std::enable_if<is_copyable_ptr<T>::value>::type> { template <typename T> struct element_type<T, typename std::enable_if<is_copyable_ptr<T>::value>::type> {
using type = typename std::pointer_traits<T>::element_type; using type = typename std::pointer_traits<T>::element_type;
@ -1022,7 +1030,9 @@ template <typename T> struct element_type<T, typename std::enable_if<is_copyable
/// Combination of the element type and value type - remove pointer (including smart pointers) and get the value_type of /// Combination of the element type and value type - remove pointer (including smart pointers) and get the value_type of
/// the container /// the container
template <typename T> struct element_value_type { using type = typename element_type<T>::type::value_type; }; template <typename T> struct element_value_type {
using type = typename element_type<T>::type::value_type;
};
/// Adaptor for set-like structure: This just wraps a normal container in a few utilities that do almost nothing. /// Adaptor for set-like structure: This just wraps a normal container in a few utilities that do almost nothing.
template <typename T, typename _ = void> struct pair_adaptor : std::false_type { template <typename T, typename _ = void> struct pair_adaptor : std::false_type {
@ -1283,7 +1293,9 @@ auto value_string(const T &value) -> decltype(to_string(value)) {
} }
/// template to get the underlying value type if it exists or use a default /// template to get the underlying value type if it exists or use a default
template <typename T, typename def, typename Enable = void> struct wrapped_type { using type = def; }; template <typename T, typename def, typename Enable = void> struct wrapped_type {
using type = def;
};
/// Type size for regular object types that do not look like a tuple /// Type size for regular object types that do not look like a tuple
template <typename T, typename def> struct wrapped_type<T, def, typename std::enable_if<is_wrapper<T>::value>::type> { template <typename T, typename def> struct wrapped_type<T, def, typename std::enable_if<is_wrapper<T>::value>::type> {
@ -1291,7 +1303,9 @@ template <typename T, typename def> struct wrapped_type<T, def, typename std::en
}; };
/// This will only trigger for actual void type /// This will only trigger for actual void type
template <typename T, typename Enable = void> struct type_count_base { static const int value{0}; }; template <typename T, typename Enable = void> struct type_count_base {
static const int value{0};
};
/// Type size for regular object types that do not look like a tuple /// Type size for regular object types that do not look like a tuple
template <typename T> template <typename T>
@ -1321,7 +1335,9 @@ template <typename T> struct subtype_count;
template <typename T> struct subtype_count_min; template <typename T> struct subtype_count_min;
/// This will only trigger for actual void type /// This will only trigger for actual void type
template <typename T, typename Enable = void> struct type_count { static const int value{0}; }; template <typename T, typename Enable = void> struct type_count {
static const int value{0};
};
/// Type size for regular object types that do not look like a tuple /// Type size for regular object types that do not look like a tuple
template <typename T> template <typename T>
@ -1372,7 +1388,9 @@ template <typename T> struct subtype_count {
}; };
/// This will only trigger for actual void type /// This will only trigger for actual void type
template <typename T, typename Enable = void> struct type_count_min { static const int value{0}; }; template <typename T, typename Enable = void> struct type_count_min {
static const int value{0};
};
/// Type size for regular object types that do not look like a tuple /// Type size for regular object types that do not look like a tuple
template <typename T> template <typename T>
@ -1421,7 +1439,9 @@ template <typename T> struct subtype_count_min {
}; };
/// This will only trigger for actual void type /// This will only trigger for actual void type
template <typename T, typename Enable = void> struct expected_count { static const int value{0}; }; template <typename T, typename Enable = void> struct expected_count {
static const int value{0};
};
/// For most types the number of expected items is 1 /// For most types the number of expected items is 1
template <typename T> template <typename T>
@ -1725,11 +1745,15 @@ inline std::string type_name() {
/// Convert to an unsigned integral /// Convert to an unsigned integral
template <typename T, enable_if_t<std::is_unsigned<T>::value, detail::enabler> = detail::dummy> template <typename T, enable_if_t<std::is_unsigned<T>::value, detail::enabler> = detail::dummy>
bool integral_conversion(const std::string &input, T &output) noexcept { bool integral_conversion(const std::string &input, T &output) noexcept {
if(input.empty()) { if(input.empty() || input.front() == '-') {
return false; return false;
} }
char *val = nullptr; char *val = nullptr;
errno = 0;
std::uint64_t output_ll = std::strtoull(input.c_str(), &val, 0); std::uint64_t output_ll = std::strtoull(input.c_str(), &val, 0);
if(errno == ERANGE) {
return false;
}
output = static_cast<T>(output_ll); output = static_cast<T>(output_ll);
if(val == (input.c_str() + input.size()) && static_cast<std::uint64_t>(output) == output_ll) { if(val == (input.c_str() + input.size()) && static_cast<std::uint64_t>(output) == output_ll) {
return true; return true;
@ -1750,7 +1774,11 @@ bool integral_conversion(const std::string &input, T &output) noexcept {
return false; return false;
} }
char *val = nullptr; char *val = nullptr;
errno = 0;
std::int64_t output_ll = std::strtoll(input.c_str(), &val, 0); std::int64_t output_ll = std::strtoll(input.c_str(), &val, 0);
if(errno == ERANGE) {
return false;
}
output = static_cast<T>(output_ll); output = static_cast<T>(output_ll);
if(val == (input.c_str() + input.size()) && static_cast<std::int64_t>(output) == output_ll) { if(val == (input.c_str() + input.size()) && static_cast<std::int64_t>(output) == output_ll) {
return true; return true;
@ -1867,18 +1895,18 @@ bool lexical_cast(const std::string &input, T &output) {
bool worked = false; bool worked = false;
auto nloc = str1.find_last_of("+-"); auto nloc = str1.find_last_of("+-");
if(nloc != std::string::npos && nloc > 0) { if(nloc != std::string::npos && nloc > 0) {
worked = detail::lexical_cast(str1.substr(0, nloc), x); worked = lexical_cast(str1.substr(0, nloc), x);
str1 = str1.substr(nloc); str1 = str1.substr(nloc);
if(str1.back() == 'i' || str1.back() == 'j') if(str1.back() == 'i' || str1.back() == 'j')
str1.pop_back(); str1.pop_back();
worked = worked && detail::lexical_cast(str1, y); worked = worked && lexical_cast(str1, y);
} else { } else {
if(str1.back() == 'i' || str1.back() == 'j') { if(str1.back() == 'i' || str1.back() == 'j') {
str1.pop_back(); str1.pop_back();
worked = detail::lexical_cast(str1, y); worked = lexical_cast(str1, y);
x = XC{0}; x = XC{0};
} else { } else {
worked = detail::lexical_cast(str1, x); worked = lexical_cast(str1, x);
y = XC{0}; y = XC{0};
} }
} }
@ -2099,7 +2127,7 @@ template <typename AssignTo,
detail::enabler> = detail::dummy> detail::enabler> = detail::dummy>
bool lexical_assign(const std::string &input, AssignTo &output) { bool lexical_assign(const std::string &input, AssignTo &output) {
ConvertTo val{}; ConvertTo val{};
bool parse_result = (!input.empty()) ? lexical_cast<ConvertTo>(input, val) : true; bool parse_result = (!input.empty()) ? lexical_cast(input, val) : true;
if(parse_result) { if(parse_result) {
output = val; output = val;
} }
@ -2115,7 +2143,7 @@ template <
detail::enabler> = detail::dummy> detail::enabler> = detail::dummy>
bool lexical_assign(const std::string &input, AssignTo &output) { bool lexical_assign(const std::string &input, AssignTo &output) {
ConvertTo val{}; ConvertTo val{};
bool parse_result = input.empty() ? true : lexical_cast<ConvertTo>(input, val); bool parse_result = input.empty() ? true : lexical_cast(input, val);
if(parse_result) { if(parse_result) {
output = AssignTo(val); // use () form of constructor to allow some implicit conversions output = AssignTo(val); // use () form of constructor to allow some implicit conversions
} }
@ -2193,7 +2221,7 @@ bool lexical_conversion(const std::vector<std::string> &strings, AssignTo &outpu
if(str1.back() == 'i' || str1.back() == 'j') { if(str1.back() == 'i' || str1.back() == 'j') {
str1.pop_back(); str1.pop_back();
} }
auto worked = detail::lexical_cast(strings[0], x) && detail::lexical_cast(str1, y); auto worked = lexical_cast(strings[0], x) && lexical_cast(str1, y);
if(worked) { if(worked) {
output = ConvertTo{x, y}; output = ConvertTo{x, y};
} }
@ -2457,7 +2485,7 @@ inline std::string sum_string_vector(const std::vector<std::string> &values) {
std::string output; std::string output;
for(const auto &arg : values) { for(const auto &arg : values) {
double tv{0.0}; double tv{0.0};
auto comp = detail::lexical_cast<double>(arg, tv); auto comp = lexical_cast(arg, tv);
if(!comp) { if(!comp) {
try { try {
tv = static_cast<double>(detail::to_flag_value(arg)); tv = static_cast<double>(detail::to_flag_value(arg));
@ -2475,8 +2503,7 @@ inline std::string sum_string_vector(const std::vector<std::string> &values) {
} else { } else {
if(val <= static_cast<double>((std::numeric_limits<std::int64_t>::min)()) || if(val <= static_cast<double>((std::numeric_limits<std::int64_t>::min)()) ||
val >= static_cast<double>((std::numeric_limits<std::int64_t>::max)()) || val >= static_cast<double>((std::numeric_limits<std::int64_t>::max)()) ||
// NOLINTNEXTLINE(clang-diagnostic-float-equal,bugprone-narrowing-conversions) std::ceil(val) == std::floor(val)) {
val == static_cast<std::int64_t>(val)) {
output = detail::value_string(static_cast<int64_t>(val)); output = detail::value_string(static_cast<int64_t>(val));
} else { } else {
output = detail::value_string(val); output = detail::value_string(val);
@ -2998,8 +3025,9 @@ template <typename DesiredType> class TypeValidator : public Validator {
public: public:
explicit TypeValidator(const std::string &validator_name) explicit TypeValidator(const std::string &validator_name)
: Validator(validator_name, [](std::string &input_string) { : Validator(validator_name, [](std::string &input_string) {
using CLI::detail::lexical_cast;
auto val = DesiredType(); auto val = DesiredType();
if(!detail::lexical_cast(input_string, val)) { if(!lexical_cast(input_string, val)) {
return std::string("Failed parsing ") + input_string + " as a " + detail::type_name<DesiredType>(); return std::string("Failed parsing ") + input_string + " as a " + detail::type_name<DesiredType>();
} }
return std::string(); return std::string();
@ -3033,8 +3061,9 @@ class Range : public Validator {
} }
func_ = [min_val, max_val](std::string &input) { func_ = [min_val, max_val](std::string &input) {
using CLI::detail::lexical_cast;
T val; T val;
bool converted = detail::lexical_cast(input, val); bool converted = lexical_cast(input, val);
if((!converted) || (val < min_val || val > max_val)) { if((!converted) || (val < min_val || val > max_val)) {
std::stringstream out; std::stringstream out;
out << "Value " << input << " not in range ["; out << "Value " << input << " not in range [";
@ -3070,8 +3099,9 @@ class Bound : public Validator {
description(out.str()); description(out.str());
func_ = [min_val, max_val](std::string &input) { func_ = [min_val, max_val](std::string &input) {
using CLI::detail::lexical_cast;
T val; T val;
bool converted = detail::lexical_cast(input, val); bool converted = lexical_cast(input, val);
if(!converted) { if(!converted) {
return std::string("Value ") + input + " could not be converted"; return std::string("Value ") + input + " could not be converted";
} }
@ -3262,8 +3292,9 @@ class IsMember : public Validator {
// This is the function that validates // This is the function that validates
// It stores a copy of the set pointer-like, so shared_ptr will stay alive // It stores a copy of the set pointer-like, so shared_ptr will stay alive
func_ = [set, filter_fn](std::string &input) { func_ = [set, filter_fn](std::string &input) {
using CLI::detail::lexical_cast;
local_item_t b; local_item_t b;
if(!detail::lexical_cast(input, b)) { if(!lexical_cast(input, b)) {
throw ValidationError(input); // name is added later throw ValidationError(input); // name is added later
} }
if(filter_fn) { if(filter_fn) {
@ -3330,8 +3361,9 @@ class Transformer : public Validator {
desc_function_ = [mapping]() { return detail::generate_map(detail::smart_deref(mapping)); }; desc_function_ = [mapping]() { return detail::generate_map(detail::smart_deref(mapping)); };
func_ = [mapping, filter_fn](std::string &input) { func_ = [mapping, filter_fn](std::string &input) {
using CLI::detail::lexical_cast;
local_item_t b; local_item_t b;
if(!detail::lexical_cast(input, b)) { if(!lexical_cast(input, b)) {
return std::string(); return std::string();
// there is no possible way we can match anything in the mapping if we can't convert so just return // there is no possible way we can match anything in the mapping if we can't convert so just return
} }
@ -3399,8 +3431,9 @@ class CheckedTransformer : public Validator {
desc_function_ = tfunc; desc_function_ = tfunc;
func_ = [mapping, tfunc, filter_fn](std::string &input) { func_ = [mapping, tfunc, filter_fn](std::string &input) {
using CLI::detail::lexical_cast;
local_item_t b; local_item_t b;
bool converted = detail::lexical_cast(input, b); bool converted = lexical_cast(input, b);
if(converted) { if(converted) {
if(filter_fn) { if(filter_fn) {
b = filter_fn(b); b = filter_fn(b);
@ -3502,7 +3535,8 @@ class AsNumberWithUnit : public Validator {
unit = detail::to_lower(unit); unit = detail::to_lower(unit);
} }
if(unit.empty()) { if(unit.empty()) {
if(!detail::lexical_cast(input, num)) { using CLI::detail::lexical_cast;
if(!lexical_cast(input, num)) {
throw ValidationError(std::string("Value ") + input + " could not be converted to " + throw ValidationError(std::string("Value ") + input + " could not be converted to " +
detail::type_name<Number>()); detail::type_name<Number>());
} }
@ -3520,7 +3554,8 @@ class AsNumberWithUnit : public Validator {
} }
if(!input.empty()) { if(!input.empty()) {
bool converted = detail::lexical_cast(input, num); using CLI::detail::lexical_cast;
bool converted = lexical_cast(input, num);
if(!converted) { if(!converted) {
throw ValidationError(std::string("Value ") + input + " could not be converted to " + throw ValidationError(std::string("Value ") + input + " could not be converted to " +
detail::type_name<Number>()); detail::type_name<Number>());
@ -3829,7 +3864,8 @@ CLI11_INLINE IPV4Validator::IPV4Validator() : Validator("IPV4") {
} }
int num = 0; int num = 0;
for(const auto &var : result) { for(const auto &var : result) {
bool retval = detail::lexical_cast(var, num); using CLI::detail::lexical_cast;
bool retval = lexical_cast(var, num);
if(!retval) { if(!retval) {
return std::string("Failed parsing number (") + var + ')'; return std::string("Failed parsing number (") + var + ')';
} }
@ -5548,8 +5584,11 @@ struct AppFriend;
} // namespace detail } // namespace detail
namespace FailureMessage { namespace FailureMessage {
std::string simple(const App *app, const Error &e); /// Printout a clean, simple message on error (the default in CLI11 1.5+)
std::string help(const App *app, const Error &e); CLI11_INLINE std::string simple(const App *app, const Error &e);
/// Printout the full help string on error (if this fn is set, the old default for CLI11)
CLI11_INLINE std::string help(const App *app, const Error &e);
} // namespace FailureMessage } // namespace FailureMessage
/// enumeration of modes of how to deal with extras in config files /// enumeration of modes of how to deal with extras in config files
@ -6122,7 +6161,8 @@ class App {
std::string flag_description = "") { std::string flag_description = "") {
CLI::callback_t fun = [&flag_result](const CLI::results_t &res) { CLI::callback_t fun = [&flag_result](const CLI::results_t &res) {
return CLI::detail::lexical_cast(res[0], flag_result); using CLI::detail::lexical_cast;
return lexical_cast(res[0], flag_result);
}; };
auto *opt = _add_flag_internal(flag_name, std::move(fun), std::move(flag_description)); auto *opt = _add_flag_internal(flag_name, std::move(fun), std::move(flag_description));
return detail::default_flag_modifiers<T>(opt); return detail::default_flag_modifiers<T>(opt);
@ -6138,8 +6178,9 @@ class App {
CLI::callback_t fun = [&flag_results](const CLI::results_t &res) { CLI::callback_t fun = [&flag_results](const CLI::results_t &res) {
bool retval = true; bool retval = true;
for(const auto &elem : res) { for(const auto &elem : res) {
using CLI::detail::lexical_cast;
flag_results.emplace_back(); flag_results.emplace_back();
retval &= detail::lexical_cast(elem, flag_results.back()); retval &= lexical_cast(elem, flag_results.back());
} }
return retval; return retval;
}; };
@ -6851,16 +6892,6 @@ CLI11_INLINE void retire_option(App *app, const std::string &option_name);
/// Helper function to mark an option as retired /// Helper function to mark an option as retired
CLI11_INLINE void retire_option(App &app, const std::string &option_name); CLI11_INLINE void retire_option(App &app, const std::string &option_name);
namespace FailureMessage {
/// Printout a clean, simple message on error (the default in CLI11 1.5+)
CLI11_INLINE std::string simple(const App *app, const Error &e);
/// Printout the full help string on error (if this fn is set, the old default for CLI11)
CLI11_INLINE std::string help(const App *app, const Error &e);
} // namespace FailureMessage
namespace detail { namespace detail {
/// This class is simply to allow tests access to App's protected functions /// This class is simply to allow tests access to App's protected functions
struct AppFriend { struct AppFriend {
@ -7143,8 +7174,9 @@ CLI11_INLINE Option *App::add_flag_callback(std::string flag_name,
std::string flag_description) { std::string flag_description) {
CLI::callback_t fun = [function](const CLI::results_t &res) { CLI::callback_t fun = [function](const CLI::results_t &res) {
using CLI::detail::lexical_cast;
bool trigger{false}; bool trigger{false};
auto result = CLI::detail::lexical_cast(res[0], trigger); auto result = lexical_cast(res[0], trigger);
if(result && trigger) { if(result && trigger) {
function(); function();
} }
@ -7159,8 +7191,9 @@ App::add_flag_function(std::string flag_name,
std::string flag_description) { std::string flag_description) {
CLI::callback_t fun = [function](const CLI::results_t &res) { CLI::callback_t fun = [function](const CLI::results_t &res) {
using CLI::detail::lexical_cast;
std::int64_t flag_count{0}; std::int64_t flag_count{0};
CLI::detail::lexical_cast(res[0], flag_count); lexical_cast(res[0], flag_count);
function(flag_count); function(flag_count);
return true; return true;
}; };
@ -7544,7 +7577,7 @@ CLI11_NODISCARD CLI11_INLINE std::string App::help(std::string prev, AppFormatMo
// Delegate to subcommand if needed // Delegate to subcommand if needed
auto selected_subcommands = get_subcommands(); auto selected_subcommands = get_subcommands();
if(!selected_subcommands.empty()) { if(!selected_subcommands.empty()) {
return selected_subcommands.at(0)->help(prev, mode); return selected_subcommands.back()->help(prev, mode);
} }
return formatter_->make_help(this, prev, mode); return formatter_->make_help(this, prev, mode);
} }
@ -8274,7 +8307,22 @@ CLI11_INLINE bool App::_parse_single_config(const ConfigItem &item, std::size_t
if(item.inputs.size() <= 1) { if(item.inputs.size() <= 1) {
// Flag parsing // Flag parsing
auto res = config_formatter_->to_flag(item); auto res = config_formatter_->to_flag(item);
res = op->get_flag_value(item.name, res); bool converted{false};
if(op->get_disable_flag_override()) {
try {
auto val = detail::to_flag_value(res);
if(val == 1) {
res = op->get_flag_value(item.name, "{}");
converted = true;
}
} catch(...) {
}
}
if(!converted) {
res = op->get_flag_value(item.name, res);
}
op->add_result(res); op->add_result(res);
return true; return true;
@ -8998,8 +9046,9 @@ CLI11_INLINE std::string convert_arg_for_ini(const std::string &arg, char string
} }
// floating point conversion can convert some hex codes, but don't try that here // floating point conversion can convert some hex codes, but don't try that here
if(arg.compare(0, 2, "0x") != 0 && arg.compare(0, 2, "0X") != 0) { if(arg.compare(0, 2, "0x") != 0 && arg.compare(0, 2, "0X") != 0) {
using CLI::detail::lexical_cast;
double val = 0.0; double val = 0.0;
if(detail::lexical_cast(arg, val)) { if(lexical_cast(arg, val)) {
return arg; return arg;
} }
} }
@ -9420,7 +9469,7 @@ CLI11_INLINE std::string Formatter::make_description(const App *app) const {
if(min_options == 1) { if(min_options == 1) {
desc += " \n[Exactly 1 of the following options is required]"; desc += " \n[Exactly 1 of the following options is required]";
} else { } else {
desc += " \n[Exactly " + std::to_string(min_options) + "options from the following list are required]"; desc += " \n[Exactly " + std::to_string(min_options) + " options from the following list are required]";
} }
} else if(max_options > 0) { } else if(max_options > 0) {
if(min_options > 0) { if(min_options > 0) {