-
Notifications
You must be signed in to change notification settings - Fork 610
Fix issue #240: Multiple long option names / aliases #349
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
Conversation
978c27d
to
3b763c6
Compare
This looks like a good approach. Given the way short and long names are basically an alias already, I think this can mostly be reused as you have done to just make more aliases. |
1f2279d
to
a2c4b7f
Compare
* We now use a vector of long option names instead of a single name * When specifying an option, you can provide multiple names separated by commas, at most one of which may have a length of 1 (not necessarily the first specified name). The length-1 name is the single-hyphen switch (the "short name"). * Hashing uses the first long name * Option help currently only uses the first long name. Not sure if that should change. * Almost no tests for this functionality yet * Using `std::token_regex_iterator` to tokenize for option-splitting
include/cxxopts.hpp
Outdated
std::match_results<const char*> result; | ||
std::regex_match(text.c_str(), result, option_specifier); | ||
if (result.empty()) | ||
if (not std::regex_match(text.c_str(), option_specifier)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you use !
instead of not
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could... but - although personally I've long switched to only using and
, or
and not
to make my code more natural-language like.
include/cxxopts.hpp
Outdated
|
||
return std::pair<std::string, std::string>(short_sw, long_sw); | ||
constexpr const int use_non_matches { -1 }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the extra const
is redundant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jarro2783 : Only in C++11. If you compile with C++14 or later, const
isn't implied. I don't mind very much, the only reason I have this is to avoid using -1
as a "magic number".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok I didn't realise that, this is fine then.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually I think you've misread the statement about const.
A constexpr specifier used in an object declaration [or non-static member function (until C++14)] implies const
It is only non-static member functions that were dropped from implying const. So this extra const
is redundant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, that's counter-intuitive, but ok. Dropped the const.
…t/options.cpp`.
include/cxxopts.hpp
Outdated
OptionNames all_option_names = values::parser_tool::split_option_names(opts); | ||
// Note: All names will be non-empty! | ||
std::string short_sw {""}; | ||
OptionNames long_option_names = all_option_names; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do you need this extra copy? It looks like you don't use all_option_names
again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't. I just wanted to clarify this is no longer all_option_names
. Ideally, a compiler could optimize this away; but I suppose I could use std::move
. On the other hand, there are soooo many string copies in cxxopts - it could really stand to use string_view
everywhere.
include/cxxopts.hpp
Outdated
throw_or_mimic<exceptions::invalid_option_format>(opts); | ||
} | ||
short_sw = *first_length_1_name_it; | ||
long_option_names.erase(first_length_1_name_it); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems a bit complicated. Are you basically trying to find one short name and then leave multiple long names?
I wonder if a simple loop would just be clearer. Or maybe you could use remove_if
and then check if the remaining list is greater than 1.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you basically trying to find one short name and then leave multiple long names?
Yes.
I wonder if a simple loop would just be clearer.
I try to follow Sean Parent's No Raw Loops principle...
Or maybe you could use remove_if and then check if the remaining list is greater than 1.
But then I would need to re-obtain the options I've erased.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think an explanatory comment would help?
e.g. after I assign long_option_names, I could say:
// currently, this may still hold one or more single-char (=short) names; let's take care of those
alternatively, I could use std::partition
and check how many short options we have. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well remove_if doesn't actually remove it, it only moves everything to the end, so I think this is what you want.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I just read that the values at the end have unspecified value, so they might get destroyed. In that case partition is probably the way to go.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have done this with std::partition
, have a look at my force-pushed change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall looking pretty good, just a few nitpick comments.
This is good, thanks for the effort. Can you just update that |
@jarro2783 : I already have updated the const line... and am now force-pushing a CHANGELOG line addition. |
* Added a `CHANGELOG.md` entry about the multiple-long-names feature. * Simplified the separation of the short name from the long names using `std::partition` * Using C-style logical operators: `!`, `&&`, `||` rather than `not`, `and`, `or` * `std::move()`ing from a vector of strings instead of just copying it. * `constexpr const` -> just `constexpr`, since that implies const, weirdly enough.
Related to jarro2783#240, jarro2783#363, and jarro2783#349
Edit 2: This PR is finalized, now with some testing
test/options.cpp
and also insrc/example.cpp
.This change is