-
Notifications
You must be signed in to change notification settings - Fork 354
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
CLI::IsMember #222
CLI::IsMember #222
Conversation
@phlptp There are also some warnings about unsigned vs. signed comparison fixed - though it looks like you may have already fixed them in your PR, that's fine (that will probably be done before this one by quite a bit). Feedback on the design welcome. :) |
Just kind of poking around at things, but I was wondering if it might make sense to have the set member checking run through a Validator instead of in the lambda function. Then whatever add_set functions in the App class are just wrappers for add_option, and a ->check(CLI::IsMember(CLI::set)) Could even add CLI::IsNotMember(CLI::set) quite easily, then those Validators could have overloads for set or shared_ptr to a set. or chain set unions or whatever else you can do with Validators. It is a little bigger API change but it would probably clear out some additional code from App.hpp. Just a thought. |
That could work! It maps what you are doing (validating an option is in a set) much better than a special add function. That might even work around the need to have a mutable set at all, since you can add the set later on after creation. (Changing it would not be easy though, if you really needed it changed for multiple parses) |
I think in the case of reparsing, such as a terminal program or script interpreter, having a mutable set be a little more directly modifiable is probably useful, since you could very well change it between parses or even inside the callbacks themselves. Then I think supporting a shared_ptr to a set in an overload of the approvpriate Validator would make things straightforward. |
f4da463
to
ca03b06
Compare
Codecov Report
@@ Coverage Diff @@
## master #222 +/- ##
==========================================
- Coverage 100% 99.65% -0.35%
==========================================
Files 12 13 +1
Lines 2133 2054 -79
==========================================
- Hits 2133 2047 -86
- Misses 0 7 +7
Continue to review full report at Codecov.
|
Codecov Report
@@ Coverage Diff @@
## master #222 +/- ##
=====================================
Coverage 100% 100%
=====================================
Files 12 12
Lines 2133 2057 -76
=====================================
- Hits 2133 2057 -76
Continue to review full report at Codecov.
|
For posterity, here's the old plan. PR now completely rewritten based on @phlptp's idea. This is the start of CLI::Set. This fixes the set lifetime and mutability issues #170 and makes sets easy to modify after adding. It also combines case insensitivity and underscore ignoring into one package, without the massive number of methods on App needed now. In the draft:
And, of course, it needs feedback. This should give an idea of some ways to use: app.add_cli_set("--name", name, {"tom", "harry"});
CLI::Set names("tom", "harry");
names->case_insensitive();
app.add_cli_set("--name", name, names);
names.insert("john"); |
@henryiii I was tinkering with a few methods of making flags a little more generalized by extending the default false syntax a little. I think it will address #123 and #124. I want to try it out in a real application to try to work out any issues. I have a couple situations where the combination of the recent features will hopefully be able to collapse a bunch of different options into a single one. I want to wait until the set is a little more developed to make a PR on it though as I think it will interact nicely with it. |
Sounds good. This is nearly ready, just needs more testing and docs. I've been battling with the older compilers to get this to pass under C++11. I've had to give up nicer template errors but I think the basic syntax is intact. It might be nice to have a shortcut method for creating a simple set inline, so far it's pretty convoluted. app->add_option("--set", value)->set(CLI::case_insensitive, "one", "two", "three"); |
I don't think I can make such a shortcut syntax work on GCC 4.7, it can't tell std::function from anything else. |
I have had some luck in the past using std::is_assignable<std::function<...>,Args> to check if something is a std::function or can be converted to one. That only works if std::function<... > doesn't participate in the template type deduction though. I think that works with C++11. and use that as part of SFINAE to differentiate between function like objects and regular types |
f425f31
to
adad0de
Compare
Use IsMember now Using IsMember as backend for Set Non-const validator backend Move set tests to set Clearer inits
Tighten up classes a bit for MSVC Check with GCC 4.8 too
Dropping more type safety for older compilers Shortcut string set
Making g++ 4.7 docker image happy Fix Clang tidy issue with last commit Adding one more shortcut, adding a couple of tests
2f1925b
to
cd1c4ed
Compare
Okay, @phlptp, you can see what you think, that worked partially, but ran into the issue that So, current points for review / comments:
opt->check(CLI::IsMember({"Inline", "set", "only", "supports", "strings"}, CLI::ignore_case));
opt->choices({"Inline", "set", "only", "supports", "strings"}, CLI::ignore_case);
// vs.
opt->choices("Inline", "set", "only", "supports", "strings", CLI::ignore_case); // current version in code
Any other suggestions or review from anyone appreciated. |
My experience with gcc 4.7 and 4.8 is limited, Most of the projects I work on are currently at gcc 4.9+ to enable some of the C++14 stuff and we are starting to think about dropping 4.9 support. I would say you might consider tagging a 1.8 once things are settled a little then dropping 4.7 support in a 2.0 then you can make some bigger breaking changes if you want to. |
I don't think the uniqueness check is that important, In most cases you are looking a just a small set of options and most users won't have duplicates. |
I think it comes down to a stylistic choice. You could also consider dropping the choice member function completely. From a code readability ->check(CLI::IsMember(...)) is going to be pretty clear as to what it is doing (but slightly more verbose). Where as ->choices(...) is not bad but it is not as clear and adds more code that needs to maintained and managed and documented all to say it does the same thing as check(CLI::IsMember(...)). |
I like that idea - I'll drop choices then it can be readded based on usage later if needed. |
@SkyToGround, @kouchy, feel free to take a look too. |
a458934
to
d93a0c1
Compare
Other types should now be supported for the initializer list. I've added a helper that corrects You still need a C++ class for the container for |
@henryiii I was about to write a comment about using |
4a69602
to
6c2df1f
Compare
f8a8d7f
to
b9ef32d
Compare
bbdf90c
to
5ace964
Compare
5ace964
to
1ab1f91
Compare
Will aim for a merge tomorrow if all looks good. |
Posting these unused snippets of code for posterity, since I'll squash and merge: /// Allow a set to be quickly created
template <typename... Args> Option *choices(Args &&... args) {
check(IsMember(std::forward<Args>(args)...));
return this;
}
/// Allow a set to be quickly created (originally string only)
template <typename... Args> Option *choices(std::initializer_list<std::string> tmpset, Args &&... args) {
std::vector<std::string> myset(tmpset);
check(IsMember(myset, std::forward<Args>(args)...));
return this;
} And, the other form of the shortcut: /// Allow a set to be quickly created, strings followed optionally by functions.
///
/// Start: Will always have std::string
template <typename... Args> Option *choices(std::string value, Args &&... args) {
std::vector<std::string> values = {value};
return choices(values, std::forward<Args>(args)...);
}
private:
/// Final function called - no function
Option *choices(std::vector<std::string> values) {
check(IsMember(values));
return this;
}
/// Final function called - function present
template <typename T,
enable_if_t<std::is_assignable<std::function<std::string(std::string)>, T>::value &&
!(std::is_same<T, const char *>::value || std::is_same<T, char *>::value),
detail::enabler> = detail::dummy>
Option *choices(std::vector<std::string> values, T fn) {
check(IsMember(values, fn));
return this;
}
/// Append a string
template <typename T,
typename... Args,
enable_if_t<std::is_assignable<std::string, T>::value, detail::enabler> = detail::dummy>
Option *choices(std::vector<std::string> values, T value, Args &&... args) {
values.push_back(std::move(value));
return choices(values, std::forward<Args>(args)...);
}
/// Combine functions
template <typename... Args>
Option *choices(std::vector<std::string> values,
std::function<std::string(std::string)> fn1,
std::function<std::string(std::string)> fn2,
Args &&... args) {
std::function<std::string(std::string)> fn = [fn1, fn2](std::string val) { return fn1(fn2(val)); };
return choices(values, fn, std::forward<Args>(args)...);
}
|
This implements
CLI::IsMember
, and deprecates the defaultadd_set*
options . This fixes the set lifetime and mutability issues in #170 and makes sets easy to modify after adding if you use a (shared) pointer. It also combines case insensitivity and underscore ignoring into validators, without the massive number of methods on App needed now. You can use any iterable that supportsstd::begin
,std::end
, and has::value_type
, allowing a vector (for example) to be chosen to keep definition order - first item matches.New features:
Changes:
CLI::ValidationError
add_set*
functions use the new backend, and are deprecated, planned removal of most of them in 1.9, and all of them after that.mutable
versions may be removed even in 1.8.Ideas:
A
->choices(...)
shortcut could be added, but the syntax is still up for debate, so not in this PR. Feel free to make suggestions.Example of use:
You can use
std::shared_ptr
(that's internally what's used if you don't give a pointer-like). You can listCLI::ignore_case
orCLI::ignore_underscore
(or any other function that will be applied to both sides).Still to do:
add_set*
methods (can't be done on all of them because some are templates.)ignore_underscore
(no ending s) in the README.