October 14, 2021

On 10/13/21 7:36 PM, Andrey Zherikov wrote:

>

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).

Having done a lot of stuff with serialization and UDAs, this turns into a mess if you have multiple systems (serialization is really what you are doing here) using the same structs. So really, you are likely to have this Params struct be distinctly for parameter parsing. It's OK to have requirements such as expecting all items to be serialized by default and in a default way.

>

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")

Various ways to solve this:

  1. Have an @ignore uda
  2. UFCS label
  3. make label private (and ensure you check for that in your library code)
>

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

Possibly. Another possibility is to use WebFreak's idea and just tag them with @NamedArgument without a name, then you can opt-in via @NamedArgument: at the top (or put in a scope).

The way I approach serialization is "how would I serialize this thing if I wrote the code manually?" and use that as the default implementation. Then if you want to do things different from the default, use UDAs for that.

>

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; }
}

It's not the UDAs, it's requiring in the UDA a repeat of the name that's already introspectable. I also prefer having a default handling for something so UDAs are not needed, but this is not as important.

Also, I'll note for your example, having an explanation for "firstName" to mean "First Name" is another thing that reasonable people can disagree on, but IMO, I would just let the name speak for itself.

-Steve

October 14, 2021

On 10/13/21 9:13 PM, Andrey Zherikov wrote:

>

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

>

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

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

Yeah, there is a problem with doing this the D way, and that's with module constructors. You can run into cycles.

When I implemented database migrations in my application, I set it up so I could run module ctors that set up each migration near the location where I'm defining the data structures, but I ended up having to put them all into a dedicated migrations module to avoid the cycles.

D could definitely use a way to specify a no-dependency module ctor/dtor.

-Steve

October 17, 2021

On Thursday, 14 October 2021 at 15:03:34 UTC, Steven Schveighoffer wrote:

>

Having done a lot of stuff with serialization and UDAs, this turns into a mess if you have multiple systems (serialization is really what you are doing here) using the same structs. So really, you are likely to have this Params struct be distinctly for parameter parsing. It's OK to have requirements such as expecting all items to be serialized by default and in a default way.

Structs without UDA are now supported starting version 0.4.0. I also added this use case as a basic into "Getting started" section in readme.

October 18, 2021

On 10/17/21 6:02 PM, Andrey Zherikov wrote:

>

On Thursday, 14 October 2021 at 15:03:34 UTC, Steven Schveighoffer wrote:

>

Having done a lot of stuff with serialization and UDAs, this turns into a mess if you have multiple systems (serialization is really what you are doing here) using the same structs. So really, you are likely to have this Params struct be distinctly for parameter parsing. It's OK to have requirements such as expecting all items to be serialized by default and in a default way.

Structs without UDA are now supported starting version 0.4.0. I also added this use case as a basic into "Getting started" section in readme.

OMG this looks awesome! I will switch my code to using it...

Prepare for some PRs, I already see ways to make this better ;)

-Steve

October 19, 2021

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

>

Hi everyone,

I'm happy to announce that I've published a CLI argument parsing library - 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.

Very interesting! I was looking for something similar recently, will definitely give it a try! One thing that it'd be interested to see would be subcommand support. Check what DUB is doing for example.

October 19, 2021

On Monday, 18 October 2021 at 13:16:01 UTC, Steven Schveighoffer wrote:

>

OMG this looks awesome! I will switch my code to using it...

Glad to hear that my work is useful!

>

Prepare for some PRs, I already see ways to make this better ;)

Don't you mind sharing your ideas?

October 19, 2021

On Tuesday, 19 October 2021 at 03:49:46 UTC, Mathias LANG wrote:

>

Very interesting! I was looking for something similar recently, will definitely give it a try! One thing that it'd be interested to see would be subcommand support. Check what DUB is doing for example.

This is definitely in my todo list

October 19, 2021

On 10/19/21 6:54 AM, Andrey Zherikov wrote:

>

On Monday, 18 October 2021 at 13:16:01 UTC, Steven Schveighoffer wrote:

>

Prepare for some PRs, I already see ways to make this better ;)

Don't you mind sharing your ideas?

Just nitpicks. Like allowing @NamedArgument without parentheses. Or using @NamedArgument("b", "banana", "ban") instead of @NamedArgument(["b", "banana", "ban"])

All this is possible. It takes some effort on the library side, but makes things pleasant to use.

I'll know more when I start using it.

-Steve

October 19, 2021

On Tuesday, 19 October 2021 at 14:06:21 UTC, Steven Schveighoffer wrote:

>

Just nitpicks. Like allowing @NamedArgument without parentheses. Or using @NamedArgument("b", "banana", "ban") instead of @NamedArgument(["b", "banana", "ban"])

I did array because I think it makes sense to have @NamedArgument(names, help_text) as a shortcut for @(NamedArgument(names).HelpText(help_text)) for CLI.

October 19, 2021

On 10/19/21 10:36 AM, Andrey Zherikov wrote:

>

On Tuesday, 19 October 2021 at 14:06:21 UTC, Steven Schveighoffer wrote:

>

Just nitpicks. Like allowing @NamedArgument without parentheses. Or using @NamedArgument("b", "banana", "ban") instead of @NamedArgument(["b", "banana", "ban"])

I did array because I think it makes sense to have @NamedArgument(names, help_text) as a shortcut for @(NamedArgument(names).HelpText(help_text)) for CLI.

Just going to put this here, instead of a PR, but if you change your parameter type to:

NamedArgument(string[] names...);

Now, these all work:


NamedArgument("banana")
NamedArgument("b", "banana", "ban")
NamedArgument(["b", "banana", "ban"])

If you want to support an array + extra string, then you can add:

NamedArgument(string[] names, string helptext);

And it shouldn't conflict with the first overload.

Though, I like the explicit HelpText better (that's what I'd use, even if the convenient overload is there). UDAs that don't spell out what they are for are harder to review.

-Steve

1 2 3
Next ›   Last »