Overview
I already introduced the VRG library describing the support it provides for writing variadic functions in C. Here I'll discuss ...
For further actions, you may consider blocking this person and/or reporting abuse
Yes, long-option support is a must, especially for programs that have lots of options (for example, my own wrap). While I personally always have a short-option equivalent for every long option, eventually you end up with collisions and have to use letters that aren't mnemonic βΒ which is why having a long option helps the user. Additionally, all programs should accept the
--help
and--version
options.This may fall into personal taste, but when the user errs (e.g., forgets a required argument for an option), it's best to print an error message only about that and not a full dump of the complete usage leaving the user to have to scan a large output looking for the relevant portion of the usage for just that option.
Ideally, a robust CLI library should also have mechanisms for:
--help
, you may not specify any other option or program arguments).-v
for "verbose" output, but-vv
for more verbose output. (Though you probably can support this now by having your handler code increment a counter.)But I do agree that GNU's
getopt_long()
leaves a lot to be desired.Thanks for your comment! I'll think about long args.
Validation (including dependency between options) seems more complicated to add while keeping the current level of simplicity.
For example passing a
check
functionvrgarg
? SOmething like:vrgarg("-x\tXmas",check)
?It may be possible (paraphrasing Alan Kay) to have simple CLIs be simple, but complex CLIs be possible. I think it would be a neat trick if you could manage to do it all via a header-only library, but I think it's unlikely.
The GNU
struct option
ideally needs more fields. As forvrgarg
, a_Bool check(char const*)
function would be a start. You could have the library provide a few built-in checkers likevrgarg_check_int
,vrgard_check_double
, etc., but the user would be free to supply their own. The check function should be responsible for printing any error message since only it knows what the correct format is. The_Bool
return value would only indicate to the library that afalse
value means "stop processing" andexit(EX_USAGE)
.As you (and Alan Kay) said, It should be possible :)
It was just a dozen of lines of code, so I added the ability to specify a custom validator for each argument (possibly using additional parameters).
Have a look at the code on Github, or, if you feel inclined to do so, join the Discord server I just created to talk about
vrg
.With this custom validators it should be quite easy to implement a logic of mutual exclusion between flags.
I'm not sure that providing pre-made validators would be useful, they would probably ending up being too simple and generic (and if this is what is needed, the users might write the validator themself).
IMHO, it's a bad idea to put full-blown function definitions into a header file, even if they are declared
static
.It's also not clear why you're using
char
where_Bool
is more appropriate. Similarly, forvrg_def_s:: hasarg
, you're assumingchar
is signed β which it's not guaranteed to be. IMHO, you should use anenum
, but if you insist on using signed integers, at least usesigned char
.In general, the varargs stuff and the specific use-case for CLI arguments are too intertwined. If you want to make a varargs library, fine; if you want to make a CLI library, fine; but don't conflate them.
Thanks for reminding me about the
char
signedness. I completely forgot.I did not include
<stdbool.h>
as this would force anyone including "vrg.h" to have symbols liketrue
defined even if they do not want to.Yes, I might enforce that anyone should abide at least to the C99 standard but I can't see a benefit for me to put such a restriction.
Considering that the CLI functions will only be used where
main()
is, I thought it would be more convenient to ask the user to define a symbol before including "vrg.h" rather than having both to include "vrg.h" and link against a "vrg.o" object file. I might considering providing options for both usage. I need to think about it.As for "mixing" things, I do see the topic of handling varidic "arguments" for functions and parsing arguments for CLI very related. But maybe it's just me, I'd love to hear from others. In any case, since I want to provide a flexible interface, I would need to define the variadic function piece anyway.
Thanks for your comments, I'm now adding the long options as you suggested and I will probably add a way to specify a full "usage string" because I recognize that the automatically information generated by
vrgusage()
might be not enough. Imagine you want to provide the help In other languages than English ...I never said you needed to
#include <stdbool.h>
nor usetrue
orfalse
; I said to use_Bool
which is a type built into C99.C99 is 24 years old. I think it's perfectly fine to assume that compilers support it.
My fault, I always see using
_Bool
and includingstdbool.h
as a single thing.In any case, looking at the current code, you'll notice that now that field is used to hold a set of bit flags, not just as boolean (actually this always was the case, the old name suggested it was a simple boolean, that's way I changed it).
Now, it could make sense to have
vrg_argfound
defined as _Bool but I don't see the need.Rather, I would be very interested in your comments on the way the CLI is defined. I saw your article and your
wrap
repo.The way the CLI is defined, the short/long options are specified, etc, do they seem simple to you? Or, on the contrary, they look too confusing? WHat do you think?
If you're using bit flags, you should definitely use an
unsigned
type; or a type likeuint8_t
.I haven't given a lot of thought to how I would write an option parser, but I'd definitely have a distinct object for each option, then an array of pointers to option to pass to a function to parse all options. I don't see why varargs are needed at all.
Yes, it is an
unsigned
type, in fact.The variadic functions are needed because I wanted to offer a flexible interface. One can define an option simply as:
or may want to add a custom validator (was one of your suggestions, right?):
Similarly, for
vrgcli()
you can omit theargc
andargv
arguments if they are the idiomatic ones.I wanted the functions to be simple to use yet flexible.
Under the hood, the argument definitions are kept in a linked list rather than an array.
The general problem with varargs is they're not type-safe. I'd prefer always providing a validator even if it's a pre-defined do-nothing one (for string values).
I'm not using stdarg.h. The technique I use enforces type checking like any regular call.
Validators can have additional arguments (like in the example I used
isgreaterthen, 0
). Yes you can have different validators (isgreaterthan20
,islowerthan31
, ...) but I thought that giving the ability to specify additional parameters would have been easier for the programmer.Can you suggest a set of validators you think would be generally useful to have out of the box?
I'm going to add the possibility of translating the various generated messages so to allow the creation of CLI in languages other than English.
I really don't like translated CLI (and I'm not a native English speaker) but I understand this might be a need for certain types of tools.
What do you think?
I'm abolutely loving it. Thank you so much. π
UPDATE: vrg now allows the configuration the user messages. An example for Japanese has been added to the code on GH
UPDATE:
Just pushed a new version with support for long options and modified the article accordingly.
Thanks for your feedback. I'll be happy to hear any comment you may have.