Comment #0 by jrdemail2000-dlang — 2016-09-25T19:37:14Z
std.getopt.getopt invokes callback functions in the order they listed in the call to getopt. Instead callbacks should be invoked in the order used on the command line at run-time. This would support use cases where the user's argument order is taken into account. If there are also use cases where the existing behavior is preferred then a config option can be used.
An example to illustrate. This program has three options with callbacks. The callbacks simply print that it was called. The options can be passed any in any order on the command, and any number of times. The callbacks are called in the order listed in the code, rather than the order listed on the command line.
==== callorder.d ====
void main(string [] args)
{
import std.getopt;
import std.stdio;
void optionHandler(string option, string optionVal)
{
writefln("optionHander(%s, %s)", option, optionVal);
}
try {
auto r = getopt(
args,
"a|aa", "aaa val", &optionHandler,
"b|bb", "bbb val", &optionHandler,
"c|cc", "ccc val", &optionHandler,
);
if (r.helpWanted) {
auto helpText =
"Option handler call order test. Use options multiple times in different orders.";
defaultGetoptPrinter(helpText, r.options);
}
} catch (Exception exc) {
stderr.writeln("Error proccessing command line arguments: ", exc.msg);
}
}
======================
$ dmd callorder.d
$ ./callorder -a 1 -b 2 -c 3 --cc 4 --bb 5 --aa 6 -a 7 -b 8 -c 9
optionHander(a|aa, 1)
optionHander(a|aa, 6)
optionHander(a|aa, 7)
optionHander(b|bb, 2)
optionHander(b|bb, 5)
optionHander(b|bb, 8)
optionHander(c|cc, 3)
optionHander(c|cc, 4)
optionHander(c|cc, 9)
Comment #1 by jrdemail2000-dlang — 2016-10-15T18:31:00Z
A similar example, but using an array shared by multiple command line options. The arrays values are populated in the order specified lexically in the code. It would be better if they were populated in the order specified at run-time on the command line.
===== fillorder.d =====
void main(string [] args)
{
import std.getopt;
import std.stdio;
string[] cmdvals;
try {
auto r = getopt(
args,
"b|bb", "VAL Append VAL to cmdvals", &cmdvals,
"a|aa", "VAL Append VAL to cmdvals", &cmdvals,
"c|cc", "VAL Append VAL to cmdvals", &cmdvals,
);
if (r.helpWanted) {
defaultGetoptPrinter(
"std.getopt array fill order test. Use options multiple times in different orders.",
r.options);
return;
}
} catch (Exception exc) {
stderr.writeln("Error processing command line arguments: ", exc.msg);
return;
}
writeln("cmdvals array: ", cmdvals);
}
==========================
$ dmd fillorder.d
$ ./fillorder -a 1 -b 2 -c 3 -a 4 -b 5 -c 6
cmdvals array: ["2", "5", "1", "4", "3", "6"]
Comment #2 by jrdemail2000-dlang — 2016-12-13T01:58:19Z
I looked into adding support for processing arguments in command line (run-time) order. However, processing arguments in the compile-time specified order (getopt function call order) appears fairly baked into the design. My personal assessment is that it would be a fair undertaking. If this such an effort was done, it might be better to do it as part of a new version of getopt that was improved in other dimensions as well.
For my own code I created a wrapper over getopt that processes arguments in command line order. It works fine most of the time, but is more brittle/hacky than appropriate for phobos. The wrapper is here: https://github.com/eBay/tsv-utils-dlang/blob/master/common/src/getopt_inorder.d
Comment #3 by hsteoh — 2018-03-25T13:50:57Z
Andrei has proposed adding std.getopt.config.commandLineOrder, which must be the first argument in the list, to switch to this behaviour.
Cf. https://forum.dlang.org/post/[email protected]
Comment #4 by kinke — 2022-10-28T15:53:08Z
Just stumbled upon this, absolutely terrible.
Comment #5 by hsteoh — 2022-10-28T17:44:47Z
The last time I complained about this, Andrei said it's not important. I didn't know what it would take to convince him, so I wrote my own getopt instead. Maybe you'll have a better chance at changing this annoying behaviour?
Note that it *will* require extensive rewriting, because the current implementation is heavily keyed on this weird processing order, and it will require some non-trivial code surgery to refactor it to do things a different way.
Comment #6 by kinke — 2022-10-28T18:28:57Z
Well I'm not too keen on fixing it myself, but I have a use case (mutually-exclusive flags pair) which should make it pretty clear that it's a serious problem:
```
void main(string[] args) {
import std.getopt;
import std.typecons;
Nullable!bool useGui;
void handleCliOrGuiMode(string option) {
useGui = (option == "gui");
}
getopt(args, "cli", &handleCliOrGuiMode, "gui", &handleCliOrGuiMode);
import std.stdio;
writeln("useGui: ", useGui);
}
```
```
$ dmd -run foo.d --gui --cli
useGui: true
```
Comment #7 by robert.schadek — 2024-12-01T16:27:57Z