New library: argparse, for parsing CLI arguments

Steven Schveighoffer schveiguy at gmail.com
Thu Oct 14 15:03:34 UTC 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:
> ```d
> 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:
> ```d
> 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


More information about the Digitalmars-d-announce mailing list