Recovering options to getopt after a GetOptException is raised
Andrew Pennebaker
andrew.pennebaker at gmail.com
Sat Dec 8 20:12:37 UTC 2018
On Saturday, 8 December 2018 at 19:58:16 UTC, Andrew Pennebaker
wrote:
> On Wednesday, 7 October 2015 at 17:15:17 UTC, Charles McAnany
> wrote:
>> Friends,
>>
>> I'm a bit puzzled by the behavior of this code:
>>
>> import std.getopt;
>> import std.stdio;
>>
>> void main(string[] args){
>> string val;
>> GetoptResult opts;
>> try{
>> opts = getopt(args, std.getopt.config.required, "val",
>> "value you need to specify", &val);
>> }catch(GetOptException e){
>> writeln("You idiot. You had to give these options:");
>> writeln(opts.options);
>> }
>> }
>>
>> Expected result:
>> :) programName --without --correct --argument
>> You idiot. You had to give these options:
>> --val value you need to specify
>>
>> Actual result:
>> :) programName --without --correct --argument
>> You idiot. You had to give these options:
>> []
>>
>> It seems that the exception is thrown as soon as a missing
>> option is encountered. It would be nice if getopt would
>> populate GetoptResult.options before it threw the exception,
>> because it would make the above code work smoothly.
>> Alternatively, GetOptException could get a field containing
>> the documentation that was provided in the call to getopt:
>>
>> catch(GetOptException e){
>> writefln("You idiot. You didn't provide the required %s
>> argument (%s)", e.optionName, e.optionDocumentation);
>> }
>>
>> If there is a way to handle this cleanly, I'd appreciate it.
>> As it is, std.getopt.config.required seems like it's not very
>> useful because it emits a rather unhelpful error message
>> (containing only the name of the missing option) and I can't
>> see a way to print a more useful message without checking all
>> the options in my own code.
>>
>> Cheers,
>> Charles.
>
> Yup, I just came across the same problem. D's getopt() is
> silly. GetoptException could have included a field representing
> the option specification, for use with defaultGetoptPrinter();
> Or getopt() could have printed the -h usage and exit()'ed; Or
> std.getopt could have separated option specification
> construction vs. validation into distinct function calls, so
> that the spec could be bound to a variable for later use in
> printing the help message when a parse error might occur.
>
> But D's getopt doesn't do any of these things! Not the most
> helpful. If D at least had splats like Ruby, then we could copy
> the inputs that would go into the getopt() call to an array,
> and reuse that spec, with a ["-h"] array of user arguments, to
> actually print out a help message.
>
> Or, programmers can manually re-type the getopt() spec, yuck!
>
> Or, I can imagine adding a loop around the try/catch, so that a
> failure sets a "fail" boolean and changes the user arguments to
> ["-h"].
>
> For me, I have to gauge how much time I want to spend cleaning
> up the stack trace-style usage message on invalid user input,
> against more productive use of my time.
>
> By the way, the getopt() documentation seems to suggest that
> when users supply "-h|--help", that a usage banner is
> automatically printed. But in fact, the programmer must
> manually check the .helpWanted field on the result and manually
> run defaultGetoptPrinter(). Lame!
**Update**
Success! Apparently tuples can be expanded like splats, and then
it's easier to workaround the getopt() error handling behavior to
produce a cleaner usage message on user input error:
// CLI math tool
import arithmancy;
import core.stdc.stdlib;
import std.format;
import std.getopt;
import std.stdio;
import std.typecons;
int n;
// Show short CLI spec
void usage(string program, GetoptResult opts) {
defaultGetoptPrinter(
format("Usage: %s [OPTIONS]", program),
opts.options
);
}
// CLI entry point
version(unittest) {} else
void main(string[] args) {
immutable program = args[0];
auto spec = tuple(
std.getopt.config.required,
"n", "an integer", &n
);
try {
auto opts = getopt((args ~ spec).expand);
if (opts.helpWanted) {
usage(program, opts);
exit(0);
}
writeln("%d", addTwo(n));
} catch (GetOptException e) {
usage(program, getopt(([program, "-h"] ~ spec).expand));
exit(1);
}
}
More information about the Digitalmars-d
mailing list