Open wgwi opened 6 years ago
This is what I have done for a first check:
bool showHelp = true;
int width = 0;
std::string name;
bool doIt = false;
std::string command;
int main ( int argc, char** argv )
{
auto cli = clara::detail::Help(showHelp)
| clara::detail::Opt( width, "width" )["-w"]["--width"]("How wide should it be?")
| clara::detail::Opt( name, "name" )["-n"]["--name"]("By what name should I be known")
| clara::detail::Opt( doIt )["-d"]["--doit"]("Do the thing" )
| clara::detail::Arg( command, "command" )("which command to run").required();
auto result = cli.parse( clara::detail::Args( argc, argv ) );
if( !result )
{
std::cerr << "Error in command line: " << result.errorMessage() << std::endl;
exit(1);
}
std::cerr << "Show Help:" << showHelp << std::endl;
std::cerr << "Width:" << width << std::endl;
std::cerr << "Name:" << name << std::endl;
std::cerr << "Doit:" << doIt << std::endl;
std::cerr << "Command:" << command << std::endl;
if ( showHelp )
std::cerr << cli << std::endl;
return 0;
}
Can we verify if a parameter was provided or not? Except that if they have a default value or not?
I tried this example and it seems to compile for me - I was wondering what I need to do pass the arguments to Catch ?
@hassanfarid Sorry for the long delay, but I hadn't time to reply. I'm not the developer of Clara and I have no idea how to check this. My first thought was an invalid default value too.
@abishekh I'm afraid I don't understand your question. The arguments passed to main()
are argv
and argc
. These variables are passed to Clara in the call to cli.parse()
. After that, the parser changes all variables associated with the option behind the scenes. In my example, passing "--doit
" to the program leads to a change of the variable doIt
. After the call to cli.parse
, all of the associated variables are either default or changed. ;-) That is the reason why @hassanfarid asked for a way to check if a option was passed or not. ;-)
@hassanfarid if you bind to an optional variable (std::optional
, boost::optional
, or similar) then you can use the optionality to check if it was set or not
@philsquared with std::optional
, a "useable" default value still has to be set after calling cli.parse()
. On the other side, detecting a change by comparing the default value duplicates the usage of these defaults and doesn't detect parameters passed with this default. Nevertheless, IMO both solutions can become unclear / complex very fast.
I thought he want to have something like a "callback" (lamda) for the case the variable is written (not only changed) by Clara. But I'm not sure. ;-) ...IMO a very special requirement.
@joede I'm afraid I'm not quite following.
What do you mean by a "usable" default value?
And when you say, "still has to be set after calling cli.parse()
, do you mean set during the call? Or will be set externally afterwards... or something else?
The only thing I can think of that you might mean here is that you want to be able to distinguish between values that were set to None
in your code, vs values that were never set at all by Clara?
If so that does seem to be a special requirement. What you could do is bind to a lambda, and in there set your variable and some sort of state tracking auxiliary variable (or perhaps make it a std::variant
or boost::variant
or similar to track the different empty states).
First of all, I am not the original poster of this question. This was only my interpretation of the problem. ;-) So we must wait until @hassanfarid clarifies the problem.
int with = 100; // (1) the default value
std::optional<int> height = {}; // (2) without default
...
auto result = cli.parse( clara::detail::Args( argc, argv ) );
// variant (1)
if ( width != 100 )
// has been changed
// variant (2)
if ( height )
// has been changed
else
height = 50; // we must set the (useable) default value
If you have a lot of parameters, the list of if
-else
will get large and..... ugly. ;-)
Update: just a fast thought. I don't know a use case for that, but a solution could be to allow to pass lambda's instead of variables to Opt()
. So the user must decide if and where to store the passed arguments. Again, I don't see a use-case for that. It just came to mind...
Sorry, it sounds obvious, now that that's what you meant :-)
So you want to be able to specify a ("usable") default value in case it's not set on the CL, but also be able to tell, after the fact, if it was actually set on the CL? (and by "you" I mean @hassanfarid)?
First, it might be worth pointing out that, rather than the if
you can coalesce the default value on an optional
using .value_or()
:
height.value_or(50);
Which is slightly less verbose, but I can understand is still not quite what is wanted. You want that default value to be set at, or close to, the original declaration.
In which case I can only think of two ways - both of which are in user code (although, arguably, at least one could have out-of-the-box support to give you a leg up):
std::optional
(!
to check and *
to dereference) then it should "just work" (*)(*) to use a non-std
optional
you'll need to #define CLARA_CONFIG_OPTIONAL_TYPE <your optional type>
before #include
ing Clara.
HTH
WRT your update (which crossed with my last reply) - if I understand you correctly, you can "pass lambda's instead of variables to Opt()".
e.g.:
https://github.com/catchorg/Clara/blob/master/src/ClaraTests.cpp#L166
@philsquared thanks for your replies. Your reference to the test passing a lamba is interesting. I feared Clara couldn't determine a data type, so the lamba must convert the string parameter to the option itself. But i seems the parameter of the lamba is used in this case. Am I right? In this case I could update my example. Just to memorize it myself. ;-)
That is correct (here's the code that deduces it):
https://github.com/catchorg/Clara/blob/master/single_include/clara.hpp#L377
I appreciate this area of Clara is not really documented yet.
OK, just to have it complete. Here is an updated example. Feel free to move it into a wiki page or so.
// Small example of how to use Clara
//
// Save this snippet as `clara-sample.cpp` together with the simgle-header
// verion of Clara and call `g++` as shown below.
//
// g++ -Wall -std=c++11 -o clara-sample clara-sample.cpp
#include <string>
#include <iostream>
#include "clara.hpp"
// dont show the help by default. Use `-h or `-?` to enable it.
//
bool showHelp = false;
// this block of variables are changed by Clara with the corresponding options passed
// to the program.
//
int width = 0;
std::string name;
bool doIt = false;
std::string command;
// This is a special value which is set indirectly by an lamba function!
//
int index = 0;
int main ( int argc, char** argv )
{
auto cli = clara::detail::Help(showHelp)
| clara::detail::Opt( width, "width" )["-w"]["--width"]("How wide should it be?")
| clara::detail::Opt( name, "name" )["-n"]["--name"]("By what name should I be known")
| clara::detail::Opt( doIt )["-d"]["--doit"]("Do the thing" )
| clara::detail::Opt( [&]( int i )
{
if (i < 0 || i > 10)
return clara::detail::ParserResult::runtimeError("index must be between 0 and 10");
else {
index = i;
return clara::detail::ParserResult::ok( clara::detail::ParseResultType::Matched );
}
}, "index" )
["-i"]( "An index, which is an integer between 0 and 10, inclusive" )
| clara::detail::Arg( command, "command" )("which command to run").required();
auto result = cli.parse( clara::detail::Args( argc, argv ) );
if( !result )
{
std::cerr << "Error in command line: " << result.errorMessage() << std::endl;
return 1;
}
if ( showHelp )
{
std::cerr << cli << std::endl;
return 0;
}
// show the results!
std::cerr << "Show Help:" << showHelp << std::endl;
std::cerr << "Index:" << index << std::endl;
std::cerr << "Width:" << width << std::endl;
std::cerr << "Name:" << name << std::endl;
std::cerr << "Doit:" << doIt << std::endl;
std::cerr << "Command:" << command << std::endl;
return 0;
}
Thanks for your reference, but that is much more "modern C++" than I currently working with. ;-) I'm a long time "embedded C" coder and most C++ code I write is limited to GUI's written in FLTK or Qt5. But I'm currently look at it. Type traits and decltype
are on my TODO list. ;-)
What is .required()
used for?
It tell's the parser that this option is "required". This means, the option must be passed to the application.
Since this issue is currently a kind of only documentation, I report here my solution to parse an option that can be optional:
#include "clara.hpp"
#include <cstdlib>
#include <iostream>
#include <optional>
using namespace clara;
struct Config
{
bool help = false;
bool gui = false;
std::optional<size_t> seed;
};
int main(int argc, char* argv[])
{
Config config;
// Create the command line parser
auto cli = Help(config.help)
| Opt(config.gui)["-g"]["--gui"]("open the gui")
| Opt([&](unsigned value) { config.seed = value; }, "seed")["-s"]["--seed"](
"use a specific seed for randomness");
// Parse the command line
if (auto result = cli.parse(Args(argc, argv)); !result) {
std::cout << "Error in command line: " << result.errorMessage() << std::endl;
exit(EXIT_FAILURE);
}
if (config.help) {
std::cout << cli;
exit(EXIT_SUCCESS);
}
// Initialize the seed
if (config.seed) {
// Use the seed
// ...
}
It requires C++17.
Hello, is there a simple working demo here? With just include "clara.hpp", and a main function.