CLI11 icon indicating copy to clipboard operation
CLI11 copied to clipboard

default value for std::pair

Open quinor opened this issue 3 years ago • 7 comments

Following code does not work.

    // (...)
    float2 pre_translate;
    app.add_option<float2, std::pair<float, float>>(
        "--pre_translate", pre_translate, "translate before scaling (ie. to center the svg file)")
        ->default_val(std::pair<float, float>{0,0});
    // (...)

It compiles, but when ran fails with:

terminate called after throwing an instance of 'CLI::ArgumentMismatch'
  what():  --pre_translate: At least 2 required but received 1
zsh: IOT instruction (core dumped)  ./a.out --pre_translate 0 0

float2 is interestingly not constructable from std::pair but it works nevertheless. It works correctly if I comment out the default_val.

I'm pretty sure I'm doing something wrong. What's the correct method of specifying the default value?

quinor avatar Feb 27 '22 20:02 quinor

It looks as if there is no to_string operation for a pair, so it is failing on the validation of the default_val.
That will probably take a bit to fix that issue in the meantime I would suggest

// (...)
    float2 pre_translate;
    app.add_option<float2, std::pair<float, float>>(
        "--pre_translate", pre_translate, "translate before scaling (ie. to center the svg file)")->delimiter(',')
        ->default_str("0,0");
    // (...)

basically add the delimiter then just use the default_str to specify the default a little more directly

phlptp avatar Feb 27 '22 23:02 phlptp

Basically what you did should ideally work, but it does require there to be some way to convert the value to a string, which in the case of pair doesn't exist yet. So until that is resolved you would need to use a bit of workaround.

phlptp avatar Feb 27 '22 23:02 phlptp

Thank you for the swift response! I did a (different) workaround. I'm waiting for a fix to come in the future :)

quinor avatar Feb 28 '22 02:02 quinor

Yeah, I also couldn't make it work with strings. :-/

    using certs = std::pair<std::string, std::string>;
    certs x;
    op->add_option<certs>(
        "--verify", x,
        "[SIGNER SUBJECT] Verify the signature of the subject cert with the signer;\n"
        "by default, 'root.crt' and 'leaf.crt' are used")
        ->delimiter(' ')
        ->default_str("root.crt leaf.crt");

When I ran it with an empty option --verify it complains:

--verify: 2 required [TEXT,TEXT] missing
Run with --help for more information.

which doesn't make any sense since I have a default string value set, and BTW, [TEXT,TEXT] also doesn't make sense since I changed the delimiter.

If I do not use the --verify option at all, the x is empty anyway.

It looks like the default with complex types does not work at all, or am I missing something in the code?

ademyankov avatar Jan 12 '23 03:01 ademyankov

It looks like I didn't understand how the options works. The default value is assigned only if the option is not specified in the command line arguments. Otherwise, it is required to provide a value for the option.

And the ->default_str() is not the right function to use, it has to be ->default_val()

    std::string str;
    op->add_option<std::string>(
        "-s,--str", str, "description")
        ->default_val("abc");

But in this case, the logic behind default_val() doesn't make any sense :/ since I can set the default value when I declare the variable std::string str("abc");

ademyankov avatar Jan 12 '23 18:01 ademyankov

The default_val/str is used in a few cases

  1. When displaying a value in help - it can be derived from the variable used in the add_option as well.
  2. When querying the value of an option that was not given opt->results(output)
  3. When no arguments are passed to an option that allows arguments but is specified to allow none or some.

In your case if you want to use the default_str for case 3 then what is needed is

 op->add_option(
        "-s,--str", str, "description")
        ->default_val("abc")->expected(0,1); 

now in the case of pair I think we are still working out all the edge cases with default values and multivalue non-vector inputs and things like that but it should look like this

using certs = std::pair<std::string, std::string>;
    certs x;
    op->add_option(
        "--verify", x,
        "[SIGNER SUBJECT] Verify the signature of the subject cert with the signer;\n"
        "by default, 'root.crt' and 'leaf.crt' are used")
        ->delimiter(' ')
        ->default_str("root.crt leaf.crt")->expected(0,2);

the main thing you are missing is expected(0,max)

phlptp avatar Jan 12 '23 23:01 phlptp

Thank you, @phlptp ! That was exactly what I was missing. Now, I understand what it means in the documentation expected(0,1) is the equivalent of making a flag.

ademyankov avatar Jan 13 '23 16:01 ademyankov