catchorg / Clara

A simple to use, composable, command line parser for C++ 11 and beyond
Boost Software License 1.0
649 stars 67 forks source link

The placeholder in a bound option is....odd #3

Closed colonelsammy closed 6 years ago

colonelsammy commented 8 years ago

I'm a bit confused by the placeholder...what I think I want is to be able to set a default value for that field; I know that I can set it in the options struct constructor but I was expecting to be able to use the placeholder. However, that didn't work if the option never got specified...

So I left the placeholder empty (since it didn't get used) but then when I run it it throws an invalid conversion:

c:\Projects\ClaraExample\Debug>ClaraExample.exe --host
Exception: Invalid conversion
- while parsing: (--host)

c:\Projects\ClaraExample\Debug>ClaraExample.exe --host=fred
Exception: Invalid conversion
- while parsing: (--host)

This is the code:

#define CLARA_CONFIG_MAIN
#include <clara.h>
#include <iostream>

struct Opt
{
    Opt()
        : showHelp(false)
        , hostname("api.github.com")
    {}
    std::string processName;
    bool showHelp;
    std::string hostname;
};

int main(int argc, char* argv[])
{
    using namespace Clara;
    try
    {
        CommandLine<Opt> cli;

        cli.bindProcessName(&Opt::processName);
        cli["-?"]["-h"]["--help"]
            .describe("describes how to use the program")
            .bind(&Opt::showHelp);
        cli["--host"]
            .describe("host to connect to")
            .bind(&Opt::hostname, "");
            //.bind(&Opt::hostname, "must not be empty...but never gets used");

        Opt opt;
        cli.parseInto(argc, const_cast<const char**>(argv), opt);
        if (opt.showHelp)
        {
            cli.usage(std::cout, opt.processName);
        }
        else
        {
            std::cout << "Host: " << opt.hostname << std::endl;
        }
    }
    catch (const std::exception& e)
    {
        std::cerr << "Exception: " << e.what() << std::endl;
    }
    catch (...)
    {
        std::cerr << "Unknown exception..." << std::endl;
    }

    return 0;
}

I know that I can make this work by specifiying the placeholder, but it never seems to get used (using the 'bind' that is commented out above):

# This is fine...option specified on the command line...
c:\Projects\ClaraExample\Debug>ClaraExample.exe --host=fred
Host: fred

# This fails (and doesn't seem to use the placeholder)...
c:\Projects\ClaraExample\Debug>ClaraExample.exe --host
Exception: Expected argument to option: host

How do I make the placeholder useful? Can the value be 'optional'?

Is it possible/desirable to set the bound member variable (perhaps to the value of the placeholder) even if I don't specify the option on the command line (I;m not sure what side effects this might have on existing code...)?

colonelsammy commented 8 years ago

Ah...the placeholder does appear in the help text!

Not completely useless then... ;-)

philsquared commented 8 years ago

Ah! You've discovered the "lack of documentation" bug :-) Sorry about that - and sorry I didn't respond sooner. Looks like you worked it out. Is there still an outstanding question/ issue here now?

colonelsammy commented 8 years ago

I'd still like to be able to use 'placeholder' as a default but I'm wary of breaking other existing code...perhaps ArgBuilder could have a bindDefaultValue(..., defaultPlaceholder) or something like that?

[only needed for those bind()s that have a placeholder, obviously ;-) ]

philsquared commented 8 years ago

I handle default values by just setting them in the class initializer