Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support tuple #307

Merged
merged 23 commits into from
Aug 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
8bad281
add some tests with default capture on the two parameter template and…
phlptp Apr 29, 2019
0c024df
Fix some comments from Code review and add more description
phlptp May 29, 2019
ae41f36
start work on trying to clean up the type traits for which lexical ca…
phlptp May 29, 2019
b3c9b08
fix readme issue and make the condition tests a little clearer
phlptp May 30, 2019
397ce1e
add a check for out of range errors on boolean conversions
phlptp May 31, 2019
ebbeeca
Fix capitalization and some comments on option functions
phlptp Jun 3, 2019
603f01d
Allow immediate_callback on the main app to run the main app callback…
phlptp Jun 13, 2019
5a34ca3
add a is_tuple_like trait, and type_count structure for getting the n…
phlptp Jul 8, 2019
2e90d69
add lexical conversion functions for tuples and other types
phlptp Jul 10, 2019
fd990cc
remove the separate vector option and option function
phlptp Jul 11, 2019
69fd288
test out the type names for tuples
phlptp Jul 12, 2019
6831c63
add some more lexical conversion functions and test
phlptp Jul 15, 2019
b802b43
more work on tuples and tests
phlptp Jul 26, 2019
1ca0e19
Merge remote-tracking branch 'remotes/original/master' into support_t…
phlptp Jul 30, 2019
80eb779
fix some merge warnings
phlptp Jul 30, 2019
e0ad1c7
fix some typename usage and c++14 only constructs
phlptp Jul 31, 2019
a4bb051
tweak some of the template to remove undefined references
phlptp Jul 31, 2019
ceddc87
add extra static assert about is_tuple_like
phlptp Jul 31, 2019
196b66c
fix some undefined references in clang
phlptp Jul 31, 2019
e6c675e
move around some of the type_count templates to be used in the type_n…
phlptp Jul 31, 2019
490683b
move the type_count around and add some additional checks on the clas…
phlptp Jul 31, 2019
2560ea2
add some info to the readme
phlptp Aug 1, 2019
ff1679f
Merge remote-tracking branch 'remotes/original/master' into support_t…
phlptp Aug 10, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,15 +194,15 @@ While all options internally are the same type, there are several ways to add an
app.add_option(option_name, help_str="") // 🆕

app.add_option(option_name,
variable_to_bind_to, // bool, int, float, vector, 🆕 enum, or string-like, or anything with a defined conversion from a string or that takes an int🚧, double🚧, or string in a constructor.
variable_to_bind_to, // bool, int, float, vector, 🆕 enum, or string-like, or anything with a defined conversion from a string or that takes an int🚧, double🚧, or string in a constructor. Also allowed are tuples(up to 5 elements) and tuple like structures such as std::array or std::pair.
help_string="")

app.add_option_function<type>(option_name,
function <void(const type &value)>, // 🆕 int, bool, float, enum, or string-like, or anything with a defined conversion from a string, or a vector of any of the previous objects.
help_string="")

app.add_complex(... // Special case: support for complex numbers
//🚧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.
//🚧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.
app.add_option<typename T, typename XC>(option_name,
T &output, // output must be assignable or constructible from a value of type XC
help_string="")
Expand All @@ -212,7 +212,7 @@ app.add_flag(option_name,
help_string="")

app.add_flag(option_name,
variable_to_bind_to, // bool, int, 🆕 float, 🆕 vector, 🆕 enum, or 🆕 string-like, or 🆕 anything with a defined conversion from a string like add_option
variable_to_bind_to, // bool, int, 🆕 float, 🆕 vector, 🆕 enum, or 🆕 string-like, or 🆕 any singular object with a defined conversion from a string like add_option
help_string="")

app.add_flag_function(option_name, // 🆕
Expand Down Expand Up @@ -249,7 +249,7 @@ The `add_option_function<type>(...` function will typically require the template
double val
app.add_option<double,unsigned int>("-v",val);
```
which would first verify the input is convertible to an int before assigning it. Or using some variant type
which would first verify the input is convertible to an unsigned int before assigning it. Or using some variant type
```
using vtype=std::variant<int, double, std::string>;
vtype v1;
Expand All @@ -261,6 +261,13 @@ otherwise the output would default to a string. The add_option can be used with

Type such as optional<int>, optional<double>, and optional<string> are supported directly, other optional types can be added using the two parameter template. See [CLI11 Internals][] for information on how this could done and how you can add your own converters for additional types.

Vector types can also be used int the two parameter template overload
```
std::vector<double> v1;
app.add_option<std::vector<double>,int>("--vs",v1);
```
would load a vector of doubles but ensure all values can be represented as integers.

Automatic direct capture of the default string is disabled when using the two parameter template. Use `set_default_str(...)` or `->default_function(std::string())` to set the default string or capture function directly for these cases.

🆕 Flag options specified through the `add_flag*` functions allow a syntax for the option names to default particular options to a false value or any other value if some flags are passed. For example:
Expand Down
78 changes: 11 additions & 67 deletions include/CLI/App.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -468,35 +468,37 @@ class App {

/// Add option for non-vectors (duplicate copy needed without defaulted to avoid `iostream << value`)

template <typename T,
typename XC = T,
enable_if_t<!is_vector<XC>::value && !std::is_const<XC>::value, detail::enabler> = detail::dummy>
template <typename T, typename XC = T, enable_if_t<!std::is_const<XC>::value, detail::enabler> = detail::dummy>
Option *add_option(std::string option_name,
T &variable, ///< The variable to set
std::string option_description = "",
bool defaulted = false) {

auto fun = [&variable](CLI::results_t res) { // comment for spacing
return detail::lexical_assign<T, XC>(res[0], variable);
return detail::lexical_conversion<T, XC>(res, variable);
};

Option *opt = add_option(option_name, fun, option_description, defaulted, [&variable]() {
return std::string(CLI::detail::checked_to_string<T, XC>(variable));
return CLI::detail::checked_to_string<T, XC>(variable);
});
opt->type_name(detail::type_name<XC>());

// these must be actual variable since (std::max) sometimes is defined in terms of references and references to
// structs used in the evaluation can be temporary so that would cause issues.
auto Tcount = detail::type_count<T>::value;
auto XCcount = detail::type_count<XC>::value;
opt->type_size((std::max)(Tcount, XCcount));
return opt;
}

/// Add option for a callback of a specific type
template <typename T, enable_if_t<!is_vector<T>::value, detail::enabler> = detail::dummy>
template <typename T>
Option *add_option_function(std::string option_name,
const std::function<void(const T &)> &func, ///< the callback to execute
std::string option_description = "") {

auto fun = [func](CLI::results_t res) {
T variable;
bool result = detail::lexical_cast(res[0], variable);
bool result = detail::lexical_conversion<T, T>(res, variable);
if(result) {
func(variable);
}
Expand All @@ -505,6 +507,7 @@ class App {

Option *opt = add_option(option_name, std::move(fun), option_description, false);
opt->type_name(detail::type_name<T>());
opt->type_size(detail::type_count<T>::value);
return opt;
}

Expand All @@ -521,65 +524,6 @@ class App {
return add_option(option_name, CLI::callback_t(), option_description, false);
}

/// Add option for vectors
template <typename T>
Option *add_option(std::string option_name,
std::vector<T> &variable, ///< The variable vector to set
std::string option_description = "",
bool defaulted = false) {

auto fun = [&variable](CLI::results_t res) {
bool retval = true;
variable.clear();
variable.reserve(res.size());
for(const auto &elem : res) {

variable.emplace_back();
retval &= detail::lexical_cast(elem, variable.back());
}
return (!variable.empty()) && retval;
};

auto default_function = [&variable]() {
std::vector<std::string> defaults;
defaults.resize(variable.size());
std::transform(variable.begin(), variable.end(), defaults.begin(), [](T &val) {
return std::string(CLI::detail::to_string(val));
});
return std::string("[" + detail::join(defaults) + "]");
};

Option *opt = add_option(option_name, fun, option_description, defaulted, default_function);
opt->type_name(detail::type_name<T>())->type_size(-1);

return opt;
}

/// Add option for a vector callback of a specific type
template <typename T, enable_if_t<is_vector<T>::value, detail::enabler> = detail::dummy>
Option *add_option_function(std::string option_name,
const std::function<void(const T &)> &func, ///< the callback to execute
std::string option_description = "") {

CLI::callback_t fun = [func](CLI::results_t res) {
T values;
bool retval = true;
values.reserve(res.size());
for(const auto &elem : res) {
values.emplace_back();
retval &= detail::lexical_cast(elem, values.back());
}
if(retval) {
func(values);
}
return retval;
};

Option *opt = add_option(option_name, std::move(fun), std::move(option_description), false);
opt->type_name(detail::type_name<T>())->type_size(-1);
return opt;
}

/// Set a help flag, replace the existing one if present
Option *set_help_flag(std::string flag_name = "", const std::string &help_description = "") {
// take flag_description by const reference otherwise add_flag tries to assign to help_description
Expand Down
Loading