mirror of
https://github.com/CLIUtils/CLI11.git
synced 2025-04-29 12:13:52 +00:00
pair/tuple defaults (#1081)
add capability to accept pair/tuple default values and a little cleaner parsing in some cases Fixes #711 --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
5a03ee5838
commit
af270cee4a
@ -307,6 +307,30 @@ template <typename S> class is_tuple_like {
|
||||
static constexpr bool value = decltype(test<S>(0))::value;
|
||||
};
|
||||
|
||||
/// This will only trigger for actual void type
|
||||
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
|
||||
template <typename T>
|
||||
struct type_count_base<T,
|
||||
typename std::enable_if<!is_tuple_like<T>::value && !is_mutable_container<T>::value &&
|
||||
!std::is_void<T>::value>::type> {
|
||||
static constexpr int value{1};
|
||||
};
|
||||
|
||||
/// the base tuple size
|
||||
template <typename T>
|
||||
struct type_count_base<T, typename std::enable_if<is_tuple_like<T>::value && !is_mutable_container<T>::value>::type> {
|
||||
static constexpr int value{std::tuple_size<typename std::remove_reference<T>::type>::value};
|
||||
};
|
||||
|
||||
/// Type count base for containers is the type_count_base of the individual element
|
||||
template <typename T> struct type_count_base<T, typename std::enable_if<is_mutable_container<T>::value>::type> {
|
||||
static constexpr int value{type_count_base<typename T::value_type>::value};
|
||||
};
|
||||
|
||||
/// Convert an object to a string (directly forward if this can become a string)
|
||||
template <typename T, enable_if_t<std::is_convertible<T, std::string>::value, detail::enabler> = detail::dummy>
|
||||
auto to_string(T &&value) -> decltype(std::forward<T>(value)) {
|
||||
@ -332,12 +356,29 @@ std::string to_string(T &&value) {
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
/// If conversion is not supported, return an empty string (streaming is not supported for that type)
|
||||
// additional forward declarations
|
||||
|
||||
/// Print tuple value string for tuples of size ==1
|
||||
template <typename T,
|
||||
enable_if_t<!std::is_constructible<std::string, T>::value && !is_ostreamable<T>::value &&
|
||||
!is_readable_container<typename std::remove_const<T>::type>::value,
|
||||
enable_if_t<!std::is_convertible<std::string, T>::value && !std::is_constructible<std::string, T>::value &&
|
||||
!is_ostreamable<T>::value && is_tuple_like<T>::value && type_count_base<T>::value == 1,
|
||||
detail::enabler> = detail::dummy>
|
||||
std::string to_string(T &&) {
|
||||
inline std::string to_string(T &&value);
|
||||
|
||||
/// Print tuple value string for tuples of size > 1
|
||||
template <typename T,
|
||||
enable_if_t<!std::is_convertible<std::string, T>::value && !std::is_constructible<std::string, T>::value &&
|
||||
!is_ostreamable<T>::value && is_tuple_like<T>::value && type_count_base<T>::value >= 2,
|
||||
detail::enabler> = detail::dummy>
|
||||
inline std::string to_string(T &&value);
|
||||
|
||||
/// If conversion is not supported, return an empty string (streaming is not supported for that type)
|
||||
template <
|
||||
typename T,
|
||||
enable_if_t<!std::is_constructible<std::string, T>::value && !is_ostreamable<T>::value &&
|
||||
!is_readable_container<typename std::remove_const<T>::type>::value && !is_tuple_like<T>::value,
|
||||
detail::enabler> = detail::dummy>
|
||||
inline std::string to_string(T &&) {
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -346,7 +387,7 @@ template <typename T,
|
||||
enable_if_t<!std::is_constructible<std::string, T>::value && !is_ostreamable<T>::value &&
|
||||
is_readable_container<T>::value,
|
||||
detail::enabler> = detail::dummy>
|
||||
std::string to_string(T &&variable) {
|
||||
inline std::string to_string(T &&variable) {
|
||||
auto cval = variable.begin();
|
||||
auto end = variable.end();
|
||||
if(cval == end) {
|
||||
@ -360,6 +401,51 @@ std::string to_string(T &&variable) {
|
||||
return {"[" + detail::join(defaults) + "]"};
|
||||
}
|
||||
|
||||
/// Convert a tuple like object to a string
|
||||
|
||||
/// forward declarations for tuple_value_strings
|
||||
template <typename T, std::size_t I>
|
||||
inline typename std::enable_if<I == type_count_base<T>::value, std::string>::type tuple_value_string(T && /*value*/);
|
||||
|
||||
/// Recursively generate the tuple value string
|
||||
template <typename T, std::size_t I>
|
||||
inline typename std::enable_if<(I < type_count_base<T>::value), std::string>::type tuple_value_string(T &&value);
|
||||
|
||||
/// Print tuple value string for tuples of size ==1
|
||||
template <typename T,
|
||||
enable_if_t<!std::is_convertible<std::string, T>::value && !std::is_constructible<std::string, T>::value &&
|
||||
!is_ostreamable<T>::value && is_tuple_like<T>::value && type_count_base<T>::value == 1,
|
||||
detail::enabler>>
|
||||
inline std::string to_string(T &&value) {
|
||||
return to_string(std::get<0>(value));
|
||||
}
|
||||
|
||||
/// Print tuple value string for tuples of size > 1
|
||||
template <typename T,
|
||||
enable_if_t<!std::is_convertible<std::string, T>::value && !std::is_constructible<std::string, T>::value &&
|
||||
!is_ostreamable<T>::value && is_tuple_like<T>::value && type_count_base<T>::value >= 2,
|
||||
detail::enabler>>
|
||||
inline std::string to_string(T &&value) {
|
||||
auto tname = std::string(1, '[') + tuple_value_string<T, 0>(value);
|
||||
tname.push_back(']');
|
||||
return tname;
|
||||
}
|
||||
|
||||
/// Empty string if the index > tuple size
|
||||
template <typename T, std::size_t I>
|
||||
inline typename std::enable_if<I == type_count_base<T>::value, std::string>::type tuple_value_string(T && /*value*/) {
|
||||
return std::string{};
|
||||
}
|
||||
|
||||
/// Recursively generate the tuple value string
|
||||
template <typename T, std::size_t I>
|
||||
inline typename std::enable_if<(I < type_count_base<T>::value), std::string>::type tuple_value_string(T &&value) {
|
||||
auto str = std::string{to_string(std::get<I>(value))} + ',' + tuple_value_string<T, I + 1>(value);
|
||||
if(str.back() == ',')
|
||||
str.pop_back();
|
||||
return str;
|
||||
}
|
||||
|
||||
/// special template overload
|
||||
template <typename T1,
|
||||
typename T2,
|
||||
@ -404,30 +490,6 @@ template <typename T, typename def> struct wrapped_type<T, def, typename std::en
|
||||
using type = typename T::value_type;
|
||||
};
|
||||
|
||||
/// This will only trigger for actual void type
|
||||
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
|
||||
template <typename T>
|
||||
struct type_count_base<T,
|
||||
typename std::enable_if<!is_tuple_like<T>::value && !is_mutable_container<T>::value &&
|
||||
!std::is_void<T>::value>::type> {
|
||||
static constexpr int value{1};
|
||||
};
|
||||
|
||||
/// the base tuple size
|
||||
template <typename T>
|
||||
struct type_count_base<T, typename std::enable_if<is_tuple_like<T>::value && !is_mutable_container<T>::value>::type> {
|
||||
static constexpr int value{std::tuple_size<T>::value};
|
||||
};
|
||||
|
||||
/// Type count base for containers is the type_count_base of the individual element
|
||||
template <typename T> struct type_count_base<T, typename std::enable_if<is_mutable_container<T>::value>::type> {
|
||||
static constexpr int value{type_count_base<typename T::value_type>::value};
|
||||
};
|
||||
|
||||
/// Set of overloads to get the type size of an object
|
||||
|
||||
/// forward declare the subtype_count structure
|
||||
|
@ -71,25 +71,24 @@ CLI11_INLINE std::string Formatter::make_description(const App *app) const {
|
||||
std::string desc = app->get_description();
|
||||
auto min_options = app->get_require_option_min();
|
||||
auto max_options = app->get_require_option_max();
|
||||
|
||||
if(app->get_required()) {
|
||||
desc += " " + get_label("REQUIRED") + " ";
|
||||
}
|
||||
if((max_options == min_options) && (min_options > 0)) {
|
||||
if(min_options == 1) {
|
||||
desc += " \n[Exactly 1 of the following options is required]";
|
||||
|
||||
if(min_options > 0) {
|
||||
if(max_options == min_options) {
|
||||
desc += " \n[Exactly " + std::to_string(min_options) + " of the following options are required]";
|
||||
} else if(max_options > 0) {
|
||||
desc += " \n[Between " + std::to_string(min_options) + " and " + std::to_string(max_options) +
|
||||
" of the following options are required]";
|
||||
} else {
|
||||
desc += " \n[Exactly " + std::to_string(min_options) + " options from the following list are required]";
|
||||
desc += " \n[At least " + std::to_string(min_options) + " of the following options are required]";
|
||||
}
|
||||
} else if(max_options > 0) {
|
||||
if(min_options > 0) {
|
||||
desc += " \n[Between " + std::to_string(min_options) + " and " + std::to_string(max_options) +
|
||||
" of the follow options are required]";
|
||||
} else {
|
||||
desc += " \n[At most " + std::to_string(max_options) + " of the following options are allowed]";
|
||||
}
|
||||
} else if(min_options > 0) {
|
||||
desc += " \n[At least " + std::to_string(min_options) + " of the following options are required]";
|
||||
desc += " \n[At most " + std::to_string(max_options) + " of the following options are allowed]";
|
||||
}
|
||||
|
||||
return (!desc.empty()) ? desc + "\n" : std::string{};
|
||||
}
|
||||
|
||||
|
@ -662,11 +662,11 @@ CLI11_INLINE std::string Option::_validate(std::string &result, int index) const
|
||||
|
||||
CLI11_INLINE int Option::_add_result(std::string &&result, std::vector<std::string> &res) const {
|
||||
int result_count = 0;
|
||||
if(allow_extra_args_ && !result.empty() && result.front() == '[' &&
|
||||
if((allow_extra_args_ || get_expected_max() > 1) && !result.empty() && result.front() == '[' &&
|
||||
result.back() == ']') { // this is now a vector string likely from the default or user entry
|
||||
result.pop_back();
|
||||
|
||||
for(auto &var : CLI::detail::split(result.substr(1), ',')) {
|
||||
for(auto &var : CLI::detail::split_up(result.substr(1), ',')) {
|
||||
if(!var.empty()) {
|
||||
result_count += _add_result(std::move(var), res);
|
||||
}
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@ -477,6 +478,46 @@ TEST_CASE_METHOD(TApp, "OneIntFlagLike", "[app]") {
|
||||
CHECK(9 == val);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(TApp, "PairDefault", "[app]") {
|
||||
std::pair<double, std::string> pr{57.5, "test"};
|
||||
auto *opt = app.add_option("-i", pr)->expected(0, 2);
|
||||
args = {"-i"};
|
||||
run();
|
||||
CHECK(app.count("-i") == 1u);
|
||||
|
||||
std::pair<double, std::string> pr2{92.5, "t2"};
|
||||
opt->default_val(pr2);
|
||||
run();
|
||||
CHECK(pr == pr2);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(TApp, "TupleDefault", "[app]") {
|
||||
std::tuple<double, std::string, int, std::string> pr{57.5, "test", 5, "total"};
|
||||
auto *opt = app.add_option("-i", pr)->expected(0, 4);
|
||||
args = {"-i"};
|
||||
run();
|
||||
CHECK(app.count("-i") == 1u);
|
||||
|
||||
std::tuple<double, std::string, int, std::string> pr2{99.5, "test2", 87, "total3"};
|
||||
opt->default_val(pr2);
|
||||
run();
|
||||
CHECK(pr == pr2);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(TApp, "TupleComplex", "[app]") {
|
||||
std::tuple<double, std::string, int, std::pair<std::string, std::string>> pr{57.5, "test", 5, {"total", "total2"}};
|
||||
auto *opt = app.add_option("-i", pr)->expected(0, 4);
|
||||
args = {"-i"};
|
||||
run();
|
||||
CHECK(app.count("-i") == 1u);
|
||||
|
||||
std::tuple<double, std::string, int, std::pair<std::string, std::string>> pr2{
|
||||
99.5, "test2", 87, {"total3", "total4"}};
|
||||
opt->default_val(pr2);
|
||||
run();
|
||||
CHECK(pr == pr2);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(TApp, "TogetherInt", "[app]") {
|
||||
int i{0};
|
||||
app.add_option("-i,--int", i);
|
||||
|
@ -49,6 +49,11 @@ TEST_CASE("TypeTools: tuple", "[helpers]") {
|
||||
CHECK(v);
|
||||
}
|
||||
|
||||
TEST_CASE("TypeTools: tuple_to_string", "[helpers]") {
|
||||
std::pair<double, std::string> p1{0.999, "kWh"};
|
||||
CHECK(CLI::detail::to_string(p1) == "[0.999,kWh]");
|
||||
}
|
||||
|
||||
TEST_CASE("TypeTools: type_size", "[helpers]") {
|
||||
auto V = CLI::detail::type_count<int>::value;
|
||||
CHECK(1 == V);
|
||||
|
Loading…
x
Reference in New Issue
Block a user