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

Convert #54

Merged
merged 6 commits into from
Nov 25, 2017
Merged

Convert #54

merged 6 commits into from
Nov 25, 2017

Conversation

henryiii
Copy link
Collaborator

@henryiii henryiii commented Nov 24, 2017

This is a requested feature: The ability for a transform function. Here is the design suggested:

CLI::App main;
std::string path;
main.add_option("--path", path, "description")
  ->check(CLI::ExistingFile)
  ->transform([](std::string val){
          fs::path p = val;
          return fs::absolute(p).string();
});

This is internally implemented by giving all validation functions a reference instead of a copy (slight API change), and making transform simply the exact same thing as check, but with the const removed and the function rewritten as a string(string) function. CLI11 has always supported a series of validators, so this is not a major change. The validators now run first before the value is filled.

  • This might be a good opportunity to add nicer errors for failed validation, since the API is changing a little.
  • I could also transform the transform API only to string(string). Thoughts?

Validation functions now return a string representing an error message. If it is empty, the validation succeeds. As always, they can also throw a ValidationError.

@codecov
Copy link

codecov bot commented Nov 24, 2017

Codecov Report

Merging #54 into master will not change coverage.
The diff coverage is 100%.

Impacted file tree graph

@@          Coverage Diff          @@
##           master    #54   +/-   ##
=====================================
  Coverage     100%   100%           
=====================================
  Files           9      9           
  Lines        1298   1301    +3     
  Branches      257    257           
=====================================
+ Hits         1298   1301    +3
Impacted Files Coverage Δ
include/CLI/Validators.hpp 100% <100%> (ø) ⬆️
include/CLI/Option.hpp 100% <100%> (ø) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update c6fd8f4...92bc43e. Read the comment docs.

@codecov
Copy link

codecov bot commented Nov 24, 2017

Codecov Report

Merging #54 into master will not change coverage.
The diff coverage is 100%.

Impacted file tree graph

@@          Coverage Diff          @@
##           master    #54   +/-   ##
=====================================
  Coverage     100%   100%           
=====================================
  Files           9      9           
  Lines        1298   1304    +6     
  Branches      257    258    +1     
=====================================
+ Hits         1298   1304    +6
Impacted Files Coverage Δ
include/CLI/Option.hpp 100% <100%> (ø) ⬆️
include/CLI/Validators.hpp 100% <100%> (ø) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update c6fd8f4...be31a79. Read the comment docs.

@henryiii
Copy link
Collaborator Author

@Warchant, what do you think?

@Warchant
Copy link

Warchant commented Nov 24, 2017

@henryiii Thanks, that looks good.

However, one thing that looks strange to me is that all these methods (check, transform, etc) are not type-safe.

Method add_option receives generic type T, it whould be good to receive the same type T in check and transform.

CLI::App main;
int n;
main.add_option("--number", n, "description")
  ->check([](int n){
        return n > 0 and n < 10; // just example
    })
  ->transform([](int n){
        return n + 3; // just example
    });

Look at this function, it always receives non-parsed std::string and does lexical_cast inside.

UPD: one way to do it is

class OptionBase{...}

template <typename T>
class Option: public OptionBase {
  Option* check(std::function<bool(T)> f) {...}
  Option* transform(std::function<T(T)> f) {...}
  // function that is executed to parse arg from std::string to T, it is actually lexical_cast
  // if we have specialization for T then we may not call `parse` manually, otherwise (for complex types),
  // we call 
  Option* parse(std::function<T(std::string)> f) {...} 
}

class App {
...
std::vector<OptionBase_p> options;
...
}

I did not see all the code, maybe it is not possible to do due to other design solutions. Is it?

@henryiii
Copy link
Collaborator Author

No, it's not possible in the current design due to the fact that Option is not templated; that's how it can hold any type of Option, and they are all managed internally. The lambda function each Option stores has the reference to the final value; it is not stored anywhere else.

Since all input comes from the command line, it always starts as a string, so converting and validating on the original string is generally safe. And, if detail::lexical_cast is used, you should get the same, correct error if you try to use the value as an int, etc. in a validator/transform function. If you are trying to make a custom conversion to a new type, you probably should add a new add_option function or lexical_cast specialization instead of ->transform.

I originally tried to make CLI11 templated very much like you outlined, and it failed, but don't remember exactly why. Maybe due to the fact you can't change the signature of an overloaded function, and CRTP doesn't work if you want to store and use the pointers as a single base class.

I may revisit and add docs for adding specializations, though.

@henryiii
Copy link
Collaborator Author

PS: The current design also means a validator can throw a different/better error if it can't be converted, and a transform function can make a non-valid input into a valid one, like stripping the letters from complex numbers or vectors.

@henryiii henryiii merged commit d28c230 into master Nov 25, 2017
@henryiii henryiii deleted the convert branch November 25, 2017 02:11
@henryiii
Copy link
Collaborator Author

I may revisit templated options. It does seem like it could work, so I at least need to document why it doesn't work if it really doesn't...

@henryiii henryiii added this to the v1.3 milestone Nov 25, 2017
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