October 13, 2021
On 2021-10-13 7:27, Andrey Zherikov wrote:
> Hi everyone,
> 
> I'm happy to announce that I've published a CLI argument parsing library - [argparse](https://code.dlang.org/packages/argparse). It's been around for some time already so please take a look and provide your feedback if you haven't done so.
> 
> The reasoning to create one more CLI parsing library is that the existing libraries do not provide enough flexibility in parsing that I'm looking for and/or they depend on other libraries. As a result `argparse` supports wide variety of data types including enums, callbacks and arrays as well as a fully customized argument parsing. It also doesn't depend on anything besides the standard library.
> 
> Since it's in active development (activeness depends on my availability of course), I have few things to do before making the first major release so stay tuned and/or contribute if you'd like to.

Cool!

One note - gflags (https://opensource.google/projects/gflags) allows modules to define their own flags in a decentralized manner. I've always thought this is a major feature missing from std.getopt, but never got around to it. It would be great if argparse would add such support.
October 13, 2021

On Wednesday, 13 October 2021 at 16:24:52 UTC, Steven Schveighoffer wrote:

>

The point is that I shouldn't have to tell the library the name of something that I've already given a name to.

Having them named differently on the command line than the actual field name should still be a possibility (and required in some cases, e.g. the enum case above), but honestly, the Params struct exists solely to accept command line parameters, there's no compelling need to use alternate names for the command line and the field name. If the library automatically does the right thing by default, then your code becomes simpler and more beautiful.

Not to detract from your library, because I think it's an awesome design to model using structs (one I use all the time), but the API developer in me frowns at lack of DRY. Try to focus on requiring the smallest amount of machinery/attributes possible. Every time you require extraneous pieces to get things to work, it adds another place where errors/confusion can happen.

I got your point. Omitting the name is good suggestion and I'll add this.

Regarding the detecting all members and treating them as an arguments, I see one issues so far: the struct might have other members that are not used in CLI (it can be even functions).
Consider the example when the member is renamed but the struct still provides the old name for backward compatibility:

struct T
{
    string name;

    @property string label() const { return name; }
}
pragma(msg, __traits(allMembers, T));   // tuple("name", "label")

Another example is when the struct has additional functions convenient for the users so it's not clear whether void foo() is a CLI flag or just a convenient function.

So to implement your suggestion correctly, argparse should provide a way to opt-out specific members from CLI.

In addition to that, each CLI argument usually has its own help text so in most cases each member will have an UDA with this text which makes opt-out approach mush less useful. So it sill look like this at the end:

struct T
{
    @help("First name")
    string firstName;

    @help("Last name")
    string lastName;

    @skip
    @property string fullName() const { return firstName~" "~lastName; }
}
October 14, 2021

On Wednesday, 13 October 2021 at 18:39:47 UTC, SealabJaster wrote:

>

On Wednesday, 13 October 2021 at 11:27:40 UTC, Andrey Zherikov wrote:

>

...

Guess you got fed up with me not updating JCLI :3

Looks great either way, I really like what you've done with the .Parse!().PreValidate!() chaining, looks clean.

I actually started with the research how this can be done and here is a result.

October 14, 2021
On Wednesday, 13 October 2021 at 19:26:49 UTC, Andrei Alexandrescu wrote:
> Cool!
>
> One note - gflags (https://opensource.google/projects/gflags) allows modules to define their own flags in a decentralized manner. I've always thought this is a major feature missing from std.getopt, but never got around to it. It would be great if argparse would add such support.

This is an interesting approach. I'm not a fan of it but I'll take a look at whether this can be supported.

October 13, 2021
On Wed, Oct 13, 2021 at 5:30 PM Andrey Zherikov via Digitalmars-d-announce < digitalmars-d-announce@puremagic.com> wrote:

> On Wednesday, 13 October 2021 at 19:26:49 UTC, Andrei Alexandrescu wrote:
> > Cool!
> >
> > One note - gflags (https://opensource.google/projects/gflags) allows modules to define their own flags in a decentralized manner. I've always thought this is a major feature missing from std.getopt, but never got around to it. It would be great if argparse would add such support.
>
> This is an interesting approach. I'm not a fan of it but I'll take a look at whether this can be supported.
>

Not sure how much change there is over "classic" gflags, but https://abseil.io/docs/cpp/guides/flags is what google now uses internally.

--bb


October 14, 2021

On Thursday, 14 October 2021 at 00:35:11 UTC, Bill Baxter wrote:

>

On Wed, Oct 13, 2021 at 5:30 PM Andrey Zherikov via Digitalmars-d-announce < digitalmars-d-announce@puremagic.com> wrote:

>

On Wednesday, 13 October 2021 at 19:26:49 UTC, Andrei Alexandrescu wrote:

>

Cool!

One note - gflags (https://opensource.google/projects/gflags) allows modules to define their own flags in a decentralized manner. I've always thought this is a major feature missing from std.getopt, but never got around to it. It would be great if argparse would add such support.

This is an interesting approach. I'm not a fan of it but I'll take a look at whether this can be supported.

Not sure how much change there is over "classic" gflags, but https://abseil.io/docs/cpp/guides/flags is what google now uses internally.

--bb

Abseil version suggests not to put flags into multiple .cpp files:

  • Allows distributed declaration and definition of flags, though this usage has drawbacks and should generally be avoided
  • Prefer to define flags only in the file containing the binary’s main() function
  • Prefer to reference flags only from within the file containing the binary’s main() function

So I'm a bit confused about supporting this use case

October 13, 2021
On Wed, Oct 13, 2021 at 6:15 PM Andrey Zherikov via Digitalmars-d-announce < digitalmars-d-announce@puremagic.com> wrote:

> On Thursday, 14 October 2021 at 00:35:11 UTC, Bill Baxter wrote:
> > On Wed, Oct 13, 2021 at 5:30 PM Andrey Zherikov via Digitalmars-d-announce < digitalmars-d-announce@puremagic.com> wrote:
> >
> >> On Wednesday, 13 October 2021 at 19:26:49 UTC, Andrei Alexandrescu wrote:
> >> > Cool!
> >> >
> >> > One note - gflags (https://opensource.google/projects/gflags) allows modules to define their own flags in a decentralized manner. I've always thought this is a major feature missing from std.getopt, but never got around to it. It would be great if argparse would add such support.
> >>
> >> This is an interesting approach. I'm not a fan of it but I'll take a look at whether this can be supported.
> >>
> >
> > Not sure how much change there is over "classic" gflags, but https://abseil.io/docs/cpp/guides/flags is what google now uses internally.
> >
> > --bb
>
> Abseil version suggests not to put flags into multiple .cpp files:
> - `Allows distributed declaration and definition of flags, though
> this usage has drawbacks and should generally be avoided`
> - `Prefer to define flags only in the file containing the
> binary’s main() function`
> - `Prefer to reference flags only from within the file containing
> the binary’s main() function`
>
> So I'm a bit confused about supporting this use case
>

Yeh, it's definitely a mixed bag.  It can be very convenient to be able to put the flag right near point of use without having to do any plumbing. But sometimes it can be frustrating given that "flags" are essentially a single global namespace that people don't always realize is a global namespace.  Quite annoying when you go to add something like a "--start_time" flag and find that some random .cc file in a library already defines that flag for their own purposes.

--bb


October 14, 2021

On Thursday, 14 October 2021 at 02:09:35 UTC, Bill Baxter wrote:

>

Yeh, it's definitely a mixed bag. It can be very convenient to be able to put the flag right near point of use without having to do any plumbing. But sometimes it can be frustrating given that "flags" are essentially a single global namespace that people don't always realize is a global namespace. Quite annoying when you go to add something like a "--start_time" flag and find that some random .cc file in a library already defines that flag for their own purposes.

--bb

I might be wrong but AFAIK D program doesn't have global namespace - it's split between modules.

Another thing is that I couldn't use allMembers without using the module name explicitly, because: __traits(isModule, __MODULE__) returns false and __traits(allMembers, __MODULE__) gives "mymodule" can't have members, "mymodule" must evaluate to either a module, a struct, an union, a class, an interface or a template instantiation

October 14, 2021

On Thursday, 14 October 2021 at 13:37:29 UTC, Andrey Zherikov wrote:

>

Another thing is that I couldn't use allMembers without using the module name explicitly, because: __traits(isModule, __MODULE__) returns false and __traits(allMembers, __MODULE__) gives "mymodule" can't have members, "mymodule" must evaluate to either a module, a struct, an union, a class, an interface or a template instantiation

You can use __traits(allMembers, mixin(__MODULE__)).

October 14, 2021

On Thursday, 14 October 2021 at 13:51:50 UTC, Paul Backus wrote:

>

On Thursday, 14 October 2021 at 13:37:29 UTC, Andrey Zherikov wrote:

>

Another thing is that I couldn't use allMembers without using the module name explicitly, because: __traits(isModule, __MODULE__) returns false and __traits(allMembers, __MODULE__) gives "mymodule" can't have members, "mymodule" must evaluate to either a module, a struct, an union, a class, an interface or a template instantiation

You can use __traits(allMembers, mixin(__MODULE__)).

How nice! Thanks!