www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.announce - New library: argparse, for parsing CLI arguments

reply Andrey Zherikov <andrey.zherikov gmail.com> writes:
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.
Oct 13 2021
next sibling parent Imperatorn <johan_forsberg_86 hotmail.com> writes:
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](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.

 [...]
Oh, that's pretty nice actually
Oct 13 2021
prev sibling next sibling parent reply WebFreak001 <d.forum webfreak.org> writes:
On Wednesday, 13 October 2021 at 11:27:40 UTC, Andrey Zherikov 
wrote:
 [...]
 It also doesn't depend on anything besides the standard library.
 [...]
if you want to drop the dependency on std.typecons : Nullable you could use https://code.dlang.org/packages/expected, where you can additionally return error values with instead of returning null on error. Doesn't cast to bool but can check for .hasError which is more explicit.
Oct 13 2021
parent reply Andrey Zherikov <andrey.zherikov gmail.com> writes:
On Wednesday, 13 October 2021 at 11:59:06 UTC, WebFreak001 wrote:
 On Wednesday, 13 October 2021 at 11:27:40 UTC, Andrey Zherikov 
 wrote:
 [...]
 It also doesn't depend on anything besides the standard 
 library.
 [...]
if you want to drop the dependency on std.typecons : Nullable you could use https://code.dlang.org/packages/expected, where you can additionally return error values with instead of returning null on error. Doesn't cast to bool but can check for .hasError which is more explicit.
This will break "doesn't depend on anything besides std" unfortunately.
Oct 13 2021
parent WebFreak001 <d.forum webfreak.org> writes:
On Wednesday, 13 October 2021 at 12:11:03 UTC, Andrey Zherikov 
wrote:
 On Wednesday, 13 October 2021 at 11:59:06 UTC, WebFreak001 
 wrote:
 On Wednesday, 13 October 2021 at 11:27:40 UTC, Andrey Zherikov 
 wrote:
 [...]
 It also doesn't depend on anything besides the standard 
 library.
 [...]
if you want to drop the dependency on std.typecons : Nullable you could use https://code.dlang.org/packages/expected, where you can additionally return error values with instead of returning null on error. Doesn't cast to bool but can check for .hasError which is more explicit.
This will break "doesn't depend on anything besides std" unfortunately.
well... if you ask me that expected package should be part of the stdlib, it has decent download statistics too :p At least would be better than creating your own Result type if you do decide to drop Nullable for something with error information I think
Oct 13 2021
prev sibling next sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 10/13/21 7:27 AM, 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.
I was literally just thinking about how something like this would be useful yesterday. It's amazing when the universe just listens to me and provides what I was hoping for! One nitpick -- you should be able to opt in using the name of the field member instead of having to write ` NamedArgument`. e.g. your `string unused` parameter requires a ` NamedArgument("unused")` which seems unnecessary. -Steve
Oct 13 2021
parent reply Andrey Zherikov <andrey.zherikov gmail.com> writes:
On Wednesday, 13 October 2021 at 14:36:30 UTC, Steven 
Schveighoffer wrote:
 One nitpick -- you should be able to opt in using the name of 
 the field member instead of having to write ` NamedArgument`. 
 e.g. your `string unused` parameter requires a 
 ` NamedArgument("unused")` which seems unnecessary.
I think `unused` word confuses a bit. I meant the argument that is skipped in command line so it has a default value (`"some default value"`). So having this is totally fine: ```d struct Params { string s; NamedArgument("num") int n; } ``` I'll rename `unused` to remove this confusion.
Oct 13 2021
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 10/13/21 11:50 AM, Andrey Zherikov wrote:
 On Wednesday, 13 October 2021 at 14:36:30 UTC, Steven Schveighoffer wrote:
 One nitpick -- you should be able to opt in using the name of the 
 field member instead of having to write ` NamedArgument`. e.g. your 
 `string unused` parameter requires a ` NamedArgument("unused")` which 
 seems unnecessary.
I think `unused` word confuses a bit. I meant the argument that is skipped in command line so it has a default value (`"some default value"`). So having this is totally fine: ```d struct Params {     string s;     NamedArgument("num")     int n; } ``` I'll rename `unused` to remove this confusion.
No, it's not a confusion about `unused`. The `array` parameter has the same issue. I meant that for named parameters, one shouldn't have to attribute them for them to be considered part of the parameters. e.g. (to replace your current code): ```d struct Params { // Positional arguments are required by default PositionalArgument(0) // override the default of a named argument string name; // Named argments are optional by default string unused = "some default value"; // Numeric types are converted automatically int num; // Boolean flags are supported bool flag; // Enums are also supported enum Enum { unset, foo, boo }; NamedArgument("enum") // required since enum is a keyword Enum enumValue; // Use array to store multiple values int[] array; // Callback with no args (flag) void cb() {} // Callback with single value void cb1(string value) { assert(value == "cb-value"); } // Callback with zero or more values void cb2(string[] value) { assert(value == ["cb-v1","cb-v2"]); } } ``` 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. -Steve
Oct 13 2021
next sibling parent WebFreak001 <d.forum webfreak.org> writes:
On Wednesday, 13 October 2021 at 16:24:52 UTC, Steven 
Schveighoffer wrote:
 On 10/13/21 11:50 AM, Andrey Zherikov wrote:
 On Wednesday, 13 October 2021 at 14:36:30 UTC, Steven [...]
No, it's not a confusion about `unused`. The `array` parameter has the same issue. I meant that for named parameters, one shouldn't have to attribute them for them to be considered part of the parameters. e.g. (to replace your current code): ```d struct Params { // Positional arguments are required by default PositionalArgument(0) // override the default of a named argument string name; // Named argments are optional by default string unused = "some default value"; // Numeric types are converted automatically int num; // Boolean flags are supported bool flag; // Enums are also supported enum Enum { unset, foo, boo }; NamedArgument("enum") // required since enum is a keyword Enum enumValue; // Use array to store multiple values int[] array; // Callback with no args (flag) void cb() {} // Callback with single value void cb1(string value) { assert(value == "cb-value"); } // Callback with zero or more values void cb2(string[] value) { assert(value == ["cb-v1","cb-v2"]); } } ``` 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. -Steve
This should probably rather be: ```d struct Params { // Positional arguments are required by default PositionalArgument(0) // override the default of a named argument string name; // Named argments are optional by default NamedArgument string unused = "some default value"; // Numeric types are converted automatically NamedArgument int num; // Boolean flags are supported NamedArgument bool flag; // Enums are also supported enum Enum { unset, foo, boo }; NamedArgument("enum") // required since enum is a keyword Enum enumValue; // Use array to store multiple values NamedArgument int[] array; // Callback with no args (flag) NamedArgument void cb() {} // Callback with single value NamedArgument void cb1(string value) { assert(value == "cb-value"); } // Callback with zero or more values NamedArgument void cb2(string[] value) { assert(value == ["cb-v1","cb-v2"]); } } ``` as otherwise the definition could be ambiguous (like are the parameters positional with automatic count or named by default?) If you don't like the repetition you could also then make it ` NamedArgument { [all my variables] }` But I'm not a fan of having everything included even without UDA
Oct 13 2021
prev sibling parent reply Andrey Zherikov <andrey.zherikov gmail.com> writes:
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: ```d 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: ```d struct T { help("First name") string firstName; help("Last name") string lastName; skip property string fullName() const { return firstName~" "~lastName; } } ```
Oct 13 2021
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
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
Oct 14 2021
parent reply Andrey Zherikov <andrey.zherikov gmail.com> writes:
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.
Oct 17 2021
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
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
Oct 18 2021
parent reply Andrey Zherikov <andrey.zherikov gmail.com> writes:
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?
Oct 19 2021
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
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
Oct 19 2021
parent reply Andrey Zherikov <andrey.zherikov gmail.com> writes:
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.
Oct 19 2021
parent Steven Schveighoffer <schveiguy gmail.com> writes:
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: ```d NamedArgument(string[] names...); ``` Now, these all work: ```d NamedArgument("banana") NamedArgument("b", "banana", "ban") NamedArgument(["b", "banana", "ban"]) ``` If you want to support an array + extra string, then you can add: ```d 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
Oct 19 2021
prev sibling next sibling parent reply SealabJaster <sealabjaster gmail.com> writes:
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. Also happy to see your penchant for documentation is still holding strong!
Oct 13 2021
parent Andrey Zherikov <andrey.zherikov gmail.com> writes:
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.
Oct 13 2021
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.com> writes:
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.
Oct 13 2021
parent reply Andrey Zherikov <andrey.zherikov gmail.com> writes:
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.
Oct 13 2021
parent reply Bill Baxter <wbaxter gmail.com> writes:
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
Oct 13 2021
parent reply Andrey Zherikov <andrey.zherikov gmail.com> writes:
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
Oct 13 2021
next sibling parent reply Bill Baxter <wbaxter gmail.com> writes:
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=E2=80=99s main() function` - `Prefer to reference flags only from within the file containing the binary=E2=80=99s 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
Oct 13 2021
parent reply Andrey Zherikov <andrey.zherikov gmail.com> writes:
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`
Oct 14 2021
parent reply Paul Backus <snarwin gmail.com> writes:
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__))`.
Oct 14 2021
parent Andrey Zherikov <andrey.zherikov gmail.com> writes:
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!
Oct 14 2021
prev sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
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
Oct 14 2021
prev sibling parent reply Mathias LANG <geod24 gmail.com> writes:
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](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.
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.
Oct 18 2021
parent Andrey Zherikov <andrey.zherikov gmail.com> writes:
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
Oct 19 2021