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

Is it possible to find out if an option was specified on the command line? #4

Open colonelsammy opened 8 years ago

colonelsammy commented 8 years ago

I would like to know if the user specified a particular command line option. I can do that by setting a bool in a bound function, but I can't help thinking that the framework already 'knows' if the option got used...I couldn't see any way to find out if it had been set though - can it be done?

My rather cludgy example code;

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

struct Opt
{
    Opt()
        : hostnameWasSet(false)
    {}
    std::string processName;
    bool hostnameWasSet;
    std::string hostname;

    void setHostname(const std::string& h)
    {
        hostnameWasSet = true;
        hostname = h;
    }
};

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

        cli.bindProcessName(&Opt::processName);
        cli["--host"]
            .describe("set the host name")
            .bind(&Opt::setHostname, "api.github.com");

        Opt opt;
        cli.parseInto(argc, const_cast<const char**>(argv), opt);
        if (opt.hostnameWasSet)
        {
            std::cout << "Hostname was set..." << opt.hostname << std::endl;
        }
        else
        {
            std::cout << "Hostname was NOT set...do stuff that doesn't need the hostname" << std::endl;
        }
    }
    catch (const std::exception& e)
    {
        std::cerr << "Exception: " << e.what() << std::endl;
    }
    catch (...)
    {
        std::cerr << "Unknown exception..." << std::endl;
    }

    return 0;
}

This is the result that I want:

c:\Projects\ClaraExample\Debug>ClaraExample.exe --host=fred
Hostname was set...fred

c:\Projects\ClaraExample\Debug>ClaraExample.exe
Hostname was NOT set...do stuff that doesn't need the hostname
philsquared commented 8 years ago

I'm considering pulling the Option type out of Catch and making it available to Clara (and all users of Clara), so you could make your field Clara::Option<std::String> hostname; and test it with: if( opt.hostname ) std::cout << "Hostname was set..." << *opt.hostname << std::end;

How would that sound to you?

colonelsammy commented 8 years ago

Yes, I think that would work pretty well...please do that!

KingDuckZ commented 6 years ago

I've achieved this by passing std::optional as the value to Opt. I implemented a trivial operator>> that simply initializes the std::optional argument with whatever T it extracts from the stream and everything works just fine. After parsing I can test if the optional value was initialized.

philsquared commented 6 years ago

I've finally implemented this in Clara (not in the single include yet - but will do a release soon). It tries to auto-detect availability of std::optional, which seems to work for GCC, but from what I have read probably doesn't for VS 2017 yet (will get to that). Either way you can override it to force support by defining:

CLARA_CONFIG_OPTIONAL_TYPE std::optional

Or you can use that to specify any alternative optional (template) type, as long as it supports the ! and * operators.