Search code examples
c++poco-libraries

Poco Option with optional argument


I have a Poco (v1.9.4) based class

class PreprocessingApp : public Poco::Util::Application

with these 2 methods:

void PreprocessingApp::defineOptions(OptionSet& options)
{
    Application::defineOptions(options);

    options.addOption(
        Option("proxy", "p", "Proxify connection")
        .required(false)
        .repeatable(false)
        .argument("the value", false)
        .callback(OptionCallback<PreprocessingApp>(this, &PreprocessingApp::handleBooleanOption))
    );

    options.addOption(
        Option("test", "t", "Test something")
        .required(true)
        .repeatable(false)
        .callback(OptionCallback<PreprocessingApp>(this, &PreprocessingApp::handleBooleanOption))
    );
}

void PreprocessingApp::handleBooleanOption(const string& name, const string& value)
{
    bool actualValue = value.empty() | value == "true" | value == "1";
    config().setBool(name, actualValue);
}

As you can see, "proxy" is a boolean option. I added ".argument("the value", false)" to allow passing argument with this option, but flagged the "required" as false to make it optional.
That way I hoped to allow this functionality:

PreprocessingApp [-p [value]] -t [value]

Both variants should work:

PreprocessingApp -p -t 
PreprocessingApp -p true -t

In reality, when debugging handleBooleanOption, the "value" is always empty "".
When switching required to true (.argument("the value", true)), the "value" is "-t" and next option processing is omitted.

Is there any solution to get it work as intended?


Solution

  • According to the Poco documentation:

    Option arguments can be specified in three ways. If a Unix short option ("-o") is given, the argument directly follows the option name, without any delimiting character or space ("-ovalue"). In default option mode, or if a Unix long option ("--option") is given, the option argument is delimited from the option name with either an equal sign ('=') or a colon (':'), as in "--option=value" or "/option:value". Finally, a required option argument can be specified on the command line after the option, delimited with a space, as in "--option value" or "-o value". The latter only works for required option arguments, not optional ones.

    Basically a required argument needs a space between the name and value whilst an optional one doesn't. So for your optional argument, specify it like this: -ptrue or --proxytrue or --proxy:true.

    For the required argument, there's another issue here. You would have thought that using the setter .required(true) with Poco::Util::Option would be enough to define a required argument in the code, but apparently not. You also have to use the setter .argument(), which by default sets required to true:

    Option & argument(
        const std::string & name,
        bool required = true
    );
    

    So change your code for the required argument to:

    options.addOption(
        Poco::Util::Option("test", "t", "Test something")
            .required(true)
            .repeatable(false)
            .argument("test")
            .callback(Poco::Util::OptionCallback<ModbusInterface>(this, &ModbusInterface::handleBooleanOption))
        );
    

    Then you should be able to call your application like this, for example:

    PreprocessingApp -ptrue -t true