March 23, 2018
On Friday, March 23, 2018 16:29:48 H. S. Teoh via Digitalmars-d wrote:
> I just ran into this seemingly small problem:
>
>   void main(string[] args) {
>       string[] searchPaths;
>       getopt(args,
>           "l", (string opt, string arg) {
>               // searches through searchPaths
>               openFile(arg);
>           },
>           "I", (string opt, string arg) {
>               searchPaths ~= arg;
>           },
>           ...
>       );
>   }
>
> Running the program with:
>
>   program -I /path/to -l myfile
>
> causes a runtime error that 'myfile' cannot be found, even though it actually exists in /path/to/*.  I thought it was odd, since obviously the -I is parsed before the -l, so searchPaths should already be set when -l is seen, right?
>
> Well, looking at the implementation of std.getopt turned up the disturbing fact that the program's argument list is actually scanned *multiple times*, one for each possible option(!).  Besides the bogonity that whether or not searchPaths will be set prior to finding -l depends on the order of arguments passed to getopt(), this also represents an O(n*m) complexity in scanning program arguments, where n = number of arguments and m = number of possible options.
>
> And this is not to mention the fact that getoptImpl is *recursive template*.  Why, oh why?
>
> Am I the only one who thinks the current implementation of getopt() is really stupid??  Can somebody please talk some sense into me, or point out something really obvious that I'm missing?

Well, to be fair, getopt is called once in the entire program, and it's abnormal for there to be very many arguments, so the big-O complexity likely isn't going to matter. I'm fairly sure that a lot more has gone into having getopt be highly functional than has gone into making it efficient.

If you can improve its performance without breaking anything, then great, but having it be a bit slow really isn't likely to matter much.

Now, if you're passing lambdas to it (which I didn't even know was possible), then having the arguments called multiple times probably is a problem and should probably be looked into. I suspect that that functionality was added later, and the getopt's design as a whole didn't necessarily take it into account. Remember that getopt has been around a while and had a fair bit bolted onto it over time. Sometimes, adding what seems like a minimal addition to existing functionality can have unintended consequences when it interacts with other stuff, and with how complex getopt is, it's not entirely surprising if it has some problems. I've found that it works fairly well overall, but I think that I've also mostly stuck to the functionality that it started with.

- Jonathan M Davis

March 24, 2018
On Sat, Mar 24, 2018 at 01:43:10PM +0000, Adam D. Ruppe via Digitalmars-d wrote:
> On Friday, 23 March 2018 at 23:29:48 UTC, H. S. Teoh wrote:
> > I just ran into this seemingly small problem:
> 
> The way I'd do this is to only use getopt to build the lists, then actually process them externally. (lol adding another loop)
> 
> string[] searchPaths;
> string[] files;
> 
> getopt(args,
>   "l", &files,
>   "I", &searchPaths
> );
> 
> foreach(file; files)
>   openFile(file);
> 
> 
> then it is clear what order your operations are done in anyway, and you have a chance to perhaps report bad syntax before actually doing any real work.
> 
> Wouldn't it be weird for example if
> 
> $ cat foo.d --help
> 
> spat out the contents followed by the help?

Touche.  This uglifies the code a bit, but meh. It's just main(), no
biggie.


T

-- 
INTEL = Only half of "intelligence".