Say you want a command line switch like --log. This should enable logging to a predefined file default.log. User should also be able to use --log=somefile.log to define where things should be logged.
I tried to achieve this with getopt, but couldn't find a way for it. Adding --log twice to getopt doesn't work as it just throws an exception if the given value doesn't fit whatever was added first.
Perhaps there are some workarounds for this such as using config.passThrough and manually handling --log. However, as this is quite common in command-line parsing, it would be a nice addition to getopt.
One way this could be achieved: allow an option that takes a string value, but can be used without a value, in which case the string is set to a default value.
Comment #1 by andrej.mitrovich — 2013-12-15T10:54:11Z
I'm thinking of the following, this current code will allow --log=value, but not --log:
-----
import std.getopt;
import std.stdio;
void main(string[] args)
{
getopt(args,
"log",
(string option, string value)
{
// store value here
writeln(value);
});
}
-----
$ rdmd test.d --log=foo
> foo
$ rdmd test.d --log
> object.Exception@C:\dmd-git\dmd2\windows\bin\..\..\src\phobos\std\getop
t.d(486): Missing value for argument --log.
So it's meant to catch bugs. However I think we can extend this, and make getopt check whether the value parameter has a default initializer:
-----
import std.getopt;
import std.stdio;
void main(string[] args)
{
getopt(args,
"log",
(string option, string value = "bar") // should allow '--log'
{
// value is either set (e.g. --log=foo), or set to "bar" (--log)
writeln(value);
});
}
-----
That way we don't break code and introduce this new feature. Andrei, thoughts?
Comment #2 by andrei — 2013-12-15T11:02:22Z
I think the default argument trick is quite neat. Andrej, would you care to implement it?
Comment #3 by blah38621 — 2013-12-15T11:19:26Z
(In reply to comment #2)
> I think the default argument trick is quite neat. Andrej, would you care to
> implement it?
I'm sure he'd love to implement it, but delegate and function pointer types don't currently keep any information on the name or default value of their parameters, meaning that this is currently impossible without declaring a function and passing it as an alias template parameter.
Comment #4 by andrei — 2013-12-15T11:24:12Z
Upon a bit more thinking, the trick is rather subtle. Perhaps it would be more straightforward to allow two lambdas with the same option, and invoke the appropriate one.
import std.getopt;
import std.stdio;
void main(string[] args)
{
getopt(args,
"log",
(string option) // should allow '--log'
{
},
"log",
(string option, string value) // should allow '--log=value'
{
});
);
}
Comment #5 by blah38621 — 2013-12-15T11:31:44Z
(In reply to comment #3)
> (In reply to comment #2)
> > I think the default argument trick is quite neat. Andrej, would you care to
> > implement it?
>
> I'm sure he'd love to implement it, but delegate and function pointer types
> don't currently keep any information on the name or default value of their
> parameters, meaning that this is currently impossible without declaring a
> function and passing it as an alias template parameter.
I just realized this probably came off a wee bit more confrontational than I meant it to, woops. Either way, I think the default value option for a delegate is a beautiful way to solve the problem, as I think having 2 separate delegates is just a bit too verbose, especially as they are likely to contain the exact same code.
Comment #6 by andrej.mitrovich — 2013-12-15T12:30:23Z
(In reply to comment #3)
> (In reply to comment #2)
> > I think the default argument trick is quite neat. Andrej, would you care to
> > implement it?
>
> I'm sure he'd love to implement it, but delegate and function pointer types
> don't currently keep any information on the name or default value of their
> parameters, meaning that this is currently impossible without declaring a
> function and passing it as an alias template parameter.
getopt is a variadic template, so it's possible to extract this information:
-----
import std.traits;
void getopt(T...)(ref string[] args, T opts)
{
static assert(ParameterDefaultValueTuple!(opts[1])[1] == "bar");
}
void main(string[] args)
{
getopt(args,
"log",
(string option, string value = "bar") { });
}
-----
Comment #7 by andrej.mitrovich — 2014-04-29T10:56:36Z