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

Fix issue #240: Multiple long option names / aliases #349

Merged
merged 3 commits into from
Jul 14, 2022

Conversation

eyalroz
Copy link
Contributor

@eyalroz eyalroz commented Jul 9, 2022

Edit 2: This PR is finalized, now with some testing

  • 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.
  • Utilizing this feature in unit tests in test/options.cpp and also in src/example.cpp.

This change is Reviewable

@eyalroz eyalroz force-pushed the fix-240 branch 3 times, most recently from 978c27d to 3b763c6 Compare July 9, 2022 21:20
@jarro2783
Copy link
Owner

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.

include/cxxopts.hpp Outdated Show resolved Hide resolved
@eyalroz eyalroz force-pushed the fix-240 branch 2 times, most recently from 1f2279d to a2c4b7f Compare July 12, 2022 18:51
@eyalroz eyalroz changed the title WIP on issue #240: Multiple long option names / aliases Fix issue #240: Multiple long option names / aliases Jul 12, 2022
* 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
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))
Copy link
Owner

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

Copy link
Contributor Author

@eyalroz eyalroz Jul 12, 2022

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.


return std::pair<std::string, std::string>(short_sw, long_sw);
constexpr const int use_non_matches { -1 };
Copy link
Owner

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.

Copy link
Contributor Author

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".

Copy link
Owner

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.

Copy link
Owner

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.

Copy link
Contributor Author

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.

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;
Copy link
Owner

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.

Copy link
Contributor Author

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.

throw_or_mimic<exceptions::invalid_option_format>(opts);
}
short_sw = *first_length_1_name_it;
long_option_names.erase(first_length_1_name_it);
Copy link
Owner

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.

Copy link
Contributor Author

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.

Copy link
Contributor Author

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?

Copy link
Owner

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.

Copy link
Owner

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.

Copy link
Contributor Author

@eyalroz eyalroz Jul 12, 2022

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.

Copy link
Owner

@jarro2783 jarro2783 left a 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.

@jarro2783
Copy link
Owner

This is good, thanks for the effort. Can you just update that const line, and can you also add an entry into the changelog?

@eyalroz
Copy link
Contributor Author

eyalroz commented Jul 13, 2022

@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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants