p-ranav / argparse

Argument Parser for Modern C++
MIT License
2.51k stars 239 forks source link

Use `scan` to parse value-like bools #117

Open KOLANICH opened 2 years ago

KOLANICH commented 2 years ago

Here is the excerpt from HydrArgs fallback lib (licensed under Unlicense, feel free to reuse)

const std::unordered_set<std::string> trueStrings{"y", "Y", "YES", "yes", "Yes", "Yea", "YEA", "yea", "On", "ON", "on", "T", "t", "True", "true", "TRUE", "1", "+"};
const std::unordered_set<std::string> falseStrings{"n", "N", "NO", "no", "No", "Nay", "NAY", "nay", "Off", "OFF", "OFF", "F", "f", "False", "false", "FALSE", "0", "-"};

bool _parseBoolValue(const std::string& argStr, bool &value){
    if(trueStrings.contains(argStr)){
        value = true;
    } else {
        if(falseStrings.contains(argStr)){
            value = false;
        } else {
            return false;
        }
    }
    return true;
}
skrobinson commented 2 years ago

Thank you for posting this. Where do you see this being used?

With a CLI, I normally think of a bool value as a flag that toggles by its use or absence. How does --flag yes or --flag no improve on plain --flag?

KOLANICH commented 2 years ago

Thank you for posting this. Where do you see this being used?

I don't really expect it be widely used anywhere except boolean positional args, and I don't really expect boolean positional args be widely used, but

  1. I have implemented it just for completeness. Some CLI parsing libs have boolean positional args, some don't. HydrArgs is an abstraction layer, unifying interfaces and features of as much libs for CLI parsing as possible
    offtop (is that taking anything that is not in C++ stdlib is a risk, and people consider using a dependency for CLI args parsing for small programms as overkill. It negatively affects quality of software. I.e. my PR using CLI11 as an arg parsing library for some software (ckati) was rejected on the basis that ... CLI11 is a dependency, and they do not want any dependencies, even vendored ones. So they continue using their rudimentary handcoded CLI args parser, without any help message at all (it was my primary motivation when I have rewriten their machinery to CLI11, because the tool was too poorly documented (obviously it was not really planned to be used by anyone except Android build system developers, but I found it handy outside of that context for other software using makefikes (though, almost useless for autotools-based projects) ) so I didn't understand how to use it, so I had to reverse the meaning of flags from the code).)

Another (and in fact the way I have come to the idea of HydrArgs) rationale is that one doesn't want to have multiple cli parsing libs. I have splitted a app into parts which are shared libs, and one depends on another. A user provides args to the runner app, which loads a shared library and passes all the cli args to it. The library #1 parses the args, puts the rest of args into an array and passes them to the next library (discovered in runtime, maybe using the arguments parsed from CLI). The next library can do the similar thing, or it can do the actual work. And we don't want each lib have own CLI parsing lib, it is just waste of memory and time and energy spent on compilation (CLI parsing libs are usually header-only).

So, when one decides for which features to guarantee support, for which make it optional, and which features not to support at all, he considers the factors such as the cost of a fallback implementation of a feature and the usefullnes of such a feature.

For HydrArgs for now it was decided (for now) to guarantee the following essential features. In general it was decided to guarantee only the very high level of the CLI and the interface facing lib developer and not guarantee minor variations of CLI and output (if one wants to use the software as a library instead of parsing CLI output he can LD_PRELOAD a special backend (not implemented yet) allowing him to send args to software in the form of objects).

Tier 1, can be assummed to be available in all the backends. We have own rudimentary stripped to bones impls of them and can construct a kinda backend of them:

Tier 2 - can be assummed to be available in all the backends relying on third-party libs (if a lib doesn't support it, we don't wrap it)

All other features are implicit (do not require app code modification) and optional and app developers cannot assumme their presence.

It is easier to remember that everything (positionality/dashedness, type, mandatoryness/optionality) composes to everything (so 4 combinations for each type) than to remember disallowed combinations and then getting upset when it turns out it is surprisingly becomes needed. So we allow users to assumme support of positional boolean args. So we need a fallback impl, since not all the libs support it.