catchorg / clara Goto Github PK
View Code? Open in Web Editor NEWA simple to use, composable, command line parser for C++ 11 and beyond
License: Boost Software License 1.0
A simple to use, composable, command line parser for C++ 11 and beyond
License: Boost Software License 1.0
Tools, which aren't a command in their own respect but rather offer several independent sub commands, are quite hard to achieve with the current state of the library. Examples are git or the windows service utility sc.
Subcommands are a good way to reduce the complexity of the commandline by separating different concerns.
On windows it is common to have multi-character option names, without needing a different way to start the option. For example: "/test". The current parser accepts the leading '/' on the option, but then it sees that it is multiple characters without a second '-', so it breaks it up into: -t -e -s -t.
I'd like a way to say "do this 'windows style', and don't require "--" to start multiple-character option names.
(I have "fixed" this in my program by looking for this case and putting in the second '-', but it's a hack. I have to put both "/test" and "/-test" as equivalent names for the option, else it won't match.)
(I have no need for composing options in one option string, unlike: ls -lrt)
This is not really an issue, more of a question, but as there doesn't seem to be any better place to discuss it, let me start it here: why do we need to use ConfigT
as template parameter everywhere? It is indeed good practice to collect all the command line options values in one struct, but looking at my existing projects (which I am thinking of migrating to Clara), I see that quite a few of them don't actually do it like this and just use local variables. And there doesn't seem to be any reason not support it in Clara too, i.e. it seems to me that the following should be possible:
int main(int argc, char const* argv[])
{
struct Options {
bool foo = false;
};
Clara::CommandLine cl; // not a template!
// It looks like this could still be made to work:
Options opts;
cl["-f"].bind(&Options::foo, opts);
// But we could also have this:
bool bar = false;
cl["-b"].bind(&bar);
cl.parse(argc, argv);
}
Granted, implementing #1 would make this less critical as I could then do
cl["-b"].bind([&](bool value) { bar = value; });
but it would still be nice to have direct support for this IMHO.
Integer parsing should support prefixed hexa/octal/binary strings:
0x10
-> 16, 010
(and 0o10
) -> 8, 0b10
-> 2
There is code to handle std::optional
via CLARA_CONFIG_OPTIONAL_TYPE
; however Opt
still is expecting an argument if none is given.
For example for logging: ./program
, run with no logging (default), ./program -l
enable logging, ./program -l file.log
enable logging to named file.
This would make sense to use std::optional
here.
For example, the default will not do anything; giving the "flag" -l
will act like a pass the log through to std::clog
; giving the optional with a filename -l /path/to/file.log
will pass through to the /path/to/file.log
.
std::ostream m_log{nullptr};
std::ofstream m_log_{};
clara::Opt{[&](std::optional<fs::path> filename) {
if (!filename) {
m_log.rdbuf(std::clog.rdbuf());
return clara::ParserResult::ok(clara::ParseResultType::Matched);
}
m_log_.open(*filename);
m_log.rdbuf(m_log_.rdbuf());
return clara::ParserResult::ok(clara::ParseResultType::Matched);
},
"filename"s}["-l"s]["--log"s]("enable logging [set log file {stderr}]"s);
Determining the real terminal width is hard to do portably, but it's pretty simple to see if the COLUMNS
env var is defined and use it instead of the hard coded 80 if it is.
I could make a PR if this is considered to be a good idea.
Hello, is there a simple working demo here? With just include "clara.hpp", and a main function.
I was trying to extract remainingTokens from InternalParseResult from failed parse, but I was not able to. It seems that remainingTokens are a pair of iterators that are no longer valid after Parser::parse() method was finished.
I think it means that return value of Parser::parser() has wrong return type, as it gives access to variables that cannot be accessed. It could be fixed by either changing the return type, make variables valid at that point, or add a new TokenStream/std::vectorstd::string member to Parser class that will contain remainingTokens after parse() is finished.
I've ended up with implementing my own class from Parser that does the last thing.
Please let me know what do you think about it (nb.: in some previous version of clara those values were easily accessible by unusedTokens() method).
It's pretty common to use slashes instead of dashes under Windows and most other command line parsing libraries support this. I think Clara should too, at least for the short options.
Hey there,
the second argument of clara::Args should be const because main(int, const char*[]) is conform to the standard.
The use of the cassert
header, and thus inclusion of the lowercase assert
macro, prevents users importing this library from having "assert" as any identifier or namespace, which doesn't tend to play well with larger projects not making use of this header, and certainly doesn't play well with C++11 unit test libraries!
A simple fix, as cassert
is essentially guaranteed to contain nothing but assert
, would be to #undef
assert at the end of the header. However the special stitch script complains because there is no visible counterpart #define assert
, making this a lot less trivial.
Just as a BoundValueRef is specialised for a std::vector; it would be useful to have the same functionality for a BoundLambda
Lines 21 to 27 in adb5ec3
<optional>
should probably be included here.
Compilation produces following warning:
./third_party/clara/clara.hpp: In member function ‘virtual clara::detail::ParserResult clara::detail::BoundFlagRefBase::setValue(const string&)’:
./third_party/clara/clara.hpp:686:24: warning: ‘flag’ may be used uninitialized in this function [-Wmaybe-uninitialized]
setFlag( flag );
~~~~~~~^~~~~~~~
I fixed it locally by initializing flag to false but there might be better ways to solve this.
Out of 4 values of ParseResultType, one is not used at all ShortCircuitSame
and ShortCircuitAll
is only possible for Options.
I tried to add and Arg with ShortCircuitAll
to be able to implement subcommands, but it simply didn't work (as Arg::parse only returns Matched or failed values, no matter what valueRef->setValue
returns).
It looks that Clara just stops parsing when encountering --
.
void parseIntoTokens( std::vector<std::string> const& args, std::vector<Token>& tokens ) {
const std::string doubleDash = "--";
for( std::size_t i = 1; i < args.size() && args[i] != doubleDash; ++i )
parseIntoTokens( args[i], tokens);
}
I guess the intended behavior (example: grep -- -b file
) is not implemented yet?
IMO silently discarding unknown options is not the right behaviour and I think it should be changed before really making the library public.
I had some special needs from one of my Arg parameter that I wanted to implement by inheriting from it and reimplementing parse method. It turned out to not work, as Arg and Opt instances are sliced in Parser class (see m_options and m_args members).
I think that either Arg and Opt could be make final
, or m_options and m_args could use some kind of pointers instead of using values to allow for own implementations of those concepts.
I'm having trouble getting Clara to recognise a single positional argument.
Adding this to ClaraTests.cpp:
TEST_CASE("Mike") {
bool flag;
std::string filename;
auto command_line_parser
= Opt(flag)["-f"]("Test flag")
+ Arg(filename, "filename")("Test data filename");
SECTION("preceding flag")
{
auto result = command_line_parser.parse({ "-f", "the-filename" });
REQUIRE(result);
REQUIRE(filename == "the-filename");
}
SECTION("following flag")
{
auto result = command_line_parser.parse({ "the-filename", "-f" });
REQUIRE(result);
REQUIRE(filename == "the-filename");
}
SECTION("no flag")
{
auto result = command_line_parser.parse({ "the-filename" });
REQUIRE(result);
REQUIRE(filename == "the-filename");
}
}
Fails with:
-------------------------------------------------------------------------------
Mike
following flag
-------------------------------------------------------------------------------
/home/mac/git/Clara/src/ClaraTests.cpp:331
...............................................................................
/home/mac/git/Clara/src/ClaraTests.cpp:335: FAILED:
REQUIRE( filename == "the-filename" )
with expansion:
"" == "the-filename"
-------------------------------------------------------------------------------
Mike
no flag
-------------------------------------------------------------------------------
/home/mac/git/Clara/src/ClaraTests.cpp:338
...............................................................................
/home/mac/git/Clara/src/ClaraTests.cpp:342: FAILED:
REQUIRE( filename == "the-filename" )
with expansion:
"" == "the-filename"
Compared the the other tests, I can't see what I'm doing wrong. :(
I can't do it myself (you'd need to make me part of Clara organization, I think), but it would be nice to have Travis CI for this.
At the very least I could write a (trivial) makefile to run the tests.
It would be useful if options that use lambdas would allow the same option being specified multiple times.
I'd like to propose to extend the handling of arguments, which I have implemented in a fork of mine (which is again based on BMBurstein's).
Specifically:
Args
handle the case when argv
doesn't contain the executable as its first element, which is useful for Win32 programsBoost has a command-line (another other) configuration processor, boost::program_options. It would be useful if the README.md here could compare and contrast clara with that.
Dear Clara authors,
First thanks for this lightweight cmdline parsing library. It's much appreciated after years of program_options. 😉
I'm using Clara and I noticed that most of the time I mistype the command line argument without space between the value and the option.
Is there an easy way or could you give some hints to make a patch, so that -j 8
but also -j8
could be parsed as 8 for a case like this :
std::optional<size_t> cpus_j;
auto cmd_args =
Opt ( cpus_j, "cpus" )
["-j"]
("How many CPU cores have to be dedicated to the build.\n");
Or is it something you don't want at all in Clara ?
Warm regards,
Hi,
in the help screen, when the description contains a carriage return (\n), the library prints it in the middle of the column. This causes the overall content to be misaligned. See the example below:
# ./my_hello_program -h
usage:
my_hello_program [<arguments> ... ] options
where options are:
-?, -h, --help display usage information
-e, --experiment <experiment> The experiment to perform. The possible
choices are:
- option1: Insert <N>
unique elements in the data structure,
then perform <M> look ups of existing
elements. Measure the total insertion &
look up time
- option2:
Create an AB tree with the defined bounds
for the leaves and the inner nodes. Measure
the loading time, the insertion time and
look up time
-
option3: Insert the elements
gradually, by doubling the size of the
data structure, and measure at each step
both the insert & search time
[...]
Note that my intention is that the options, listed below the experiment, should still appear on the rightmost column, the one that contains the description of the parameter `experiment'.
Perhaps does it exist a manner to achieve the intended behaviour without altering the source code of the library?
All the best,
Dean
I'm guessing this is just not written yet, since the spot where it'd go at the end of parse()
has a // !TBD
about it. It doesn't look like there's a super clean place to expose required/not to users of the API, since there's no magic operator()
/operator[]
left open for it, in contrast with descriptions/flags (unless you want to get cute with overloading), but even just correct behavior with the somewhat-less-pretty .required() would work for me.
If you've got an approach in mind, I'd be happy to take a whack at it since it looks like it won't take too much to do.
cc @TheOnlyRew
Hi @philsquared
There is some issues about documentation and it's also add in the roadmap.
Did you need help on this point ?
Did you already have a table of content or a structure that we can complete ?
Did you prefer complete explanation of each functionnality or just a bunch of examples ?
related : #49
Is there a way to make positional arguments mandatory without manually checking them?
cppcheck reports:
When i==arg.size(), arg[i] is out of bounds. on line 554 in clara.h
One example for this is a default output filename if none is provided.
Hi, thanks for the cool library, I'm enjoying using it and look forward to its ongoing development!
I seem to have encountered a bug, though: if the argument to a boost::path
option has white space in it, the resulting path only includes the first word. Here's a small example:
#include <boost/filesystem/path.hpp> // using boost 1.66
#include <clara.hpp> // using v1.1.1
#include <iostream>
#include <string>
using path = boost::filesystem::path;
using namespace clara;
int main(int argc, char *argv[]) {
path path_opt;
std::string string_opt;
const auto cli = Opt(path_opt, "path")["--path"]("boost path argument") |
Opt(string_opt, "string")["--str"]("string argument");
const auto result = cli.parse(Args(argc, argv));
if (!result) {
std::cerr << "Error: " << result.errorMessage() << std::endl;
exit(1);
}
std::cout << "path_opt=" << path_opt << std::endl;
std::cout << "path(string_opt)=" << path(string_opt) << std::endl;
}
Here's what happens when I compile and run... notice below that the path "with a space"
comes out to just "with"
.
$ c++ --std=c++11 -I. -o bug bug.cpp -lboost_system # Apple LLVM version 9.0.0 (clang-900.0.39.2)
$ ./bug --path "with a space" --str "with a space"
path_opt="with"
path(string_opt)="with a space"
$
Of course the workaround of using a string opt then creating a path
from that is viable, just not nearly as pretty.
It's possible that this isn't a Clara bug per se, but just that boost::path
don't have the right construction semantics to work as a Clara Opt
. If so that's a bummer -- and a little insidious, since it seems to work fine as long as there's no whitespace. And when/if you do use a path with whitespace there's no warning, just elsewhere in the app you get unexpected behavior because the path isn't what you think it is :)
The above would be very helpful, as it would allow C++11 lambda functions and/or the results of std::bind
to be used as option handlers, rather than just member or free functions, which as far as I see are the only options at present.
The following code fails to compile for me on Apple Clang (Apple LLVM version 8.0.0 (clang-800.0.42.1; Target: x86_64-apple-darwin15.6.0
):
#include <clara.hpp>
#include <string>
int main(int argc, char** argv) {
bool showHelp = false;
auto parsePlayerPos = [](const std::string& playerPosString) { /* Do something here */ };
using namespace clara;
auto commandLineParser =
Help(showHelp)
| Opt(parsePlayerPos, "player-pos")
["--player-pos"]
("Some text here");
return 0;
}
if I specify the lambda expression inline, it works, i.e.:
Opt([](const std::string& playerPosString) { /* Do something here */ }, "player-pos")
The error message I get is:
In file included from /Users/lethal-guitar/RigelEngine/src/main.cpp:20:
/Users/lethal-guitar/RigelEngine/3rd_party/clara/clara.hpp:640:12: error: invalid operands to binary expression ('std::stringstream'
(aka 'basic_stringstream<char>') and '(lambda at /Users/lethal-guitar/RigelEngine/src/main.cpp:29:25)')
ss >> target;
~~ ^ ~~~~~~
/Users/lethal-guitar/RigelEngine/3rd_party/clara/clara.hpp:690:20: note: in instantiation of function template specialization
'clara::detail::convertInto<(lambda at /Users/lethal-guitar/RigelEngine/src/main.cpp:29:25)>' requested here
return convertInto( arg, m_ref );
^
/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/memory:2125:7: note: in instantiation of member function
'clara::detail::BoundValueRef<(lambda at /Users/lethal-guitar/RigelEngine/src/main.cpp:29:25)>::setValue' requested here
class __libcpp_compressed_pair_imp<_T1, _T2, 1>
^
/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/memory:4411:26: note: in instantiation of function template specialization
'std::__1::__shared_ptr_emplace<clara::detail::BoundValueRef<(lambda at /Users/lethal-guitar/RigelEngine/src/main.cpp:29:25)>,
std::__1::allocator<clara::detail::BoundValueRef<(lambda at /Users/lethal-guitar/RigelEngine/src/main.cpp:29:25)> >
>::__shared_ptr_emplace<(lambda at /Users/lethal-guitar/RigelEngine/src/main.cpp:29:25) &>' requested here
::new(__hold2.get()) _CntrlBlk(__a2, _VSTD::forward<_Args>(__args)...);
^
/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/memory:4775:29: note: in instantiation of function template specialization
'std::__1::shared_ptr<clara::detail::BoundValueRef<(lambda at /Users/lethal-guitar/RigelEngine/src/main.cpp:29:25)>
>::make_shared<(lambda at /Users/lethal-guitar/RigelEngine/src/main.cpp:29:25) &>' requested here
return shared_ptr<_Tp>::make_shared(_VSTD::forward<_Args>(__args)...);
^
/Users/lethal-guitar/RigelEngine/3rd_party/clara/clara.hpp:817:25: note: in instantiation of function template specialization
'std::__1::make_shared<clara::detail::BoundValueRef<(lambda at /Users/lethal-guitar/RigelEngine/src/main.cpp:29:25)>, (lambda at
/Users/lethal-guitar/RigelEngine/src/main.cpp:29:25) &>' requested here
: m_ref( std::make_shared<BoundValueRef<T>>( ref ) ),
^
/Users/lethal-guitar/RigelEngine/3rd_party/clara/clara.hpp:946:50: note: in instantiation of function template specialization
'clara::detail::ParserRefImpl<clara::detail::Opt>::ParserRefImpl<(lambda at /Users/lethal-guitar/RigelEngine/src/main.cpp:29:25)>'
requested here
Opt( T &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {}
^
/Users/lethal-guitar/RigelEngine/src/main.cpp:35:7: note: in instantiation of function template specialization
'clara::detail::Opt::Opt<(lambda at /Users/lethal-guitar/RigelEngine/src/main.cpp:29:25)>' requested here
| Opt(parsePlayerPos, "player-pos")
I'm using Clara v. 1.1.2.
For background, see twitter discussion here:
https://twitter.com/ClareMacraeUK/status/949415455399268353
Summary: It would be great to be able provide some descriptive prose to the parser, and have that nicely formatted and included when writing out help text.
My case is that I'm writing out Clara's usage text twice - once on error, and once on --help, and I'd like to insert some text between Clara's "usage:" and "where the options are:" lines.
It would be used to be able to write options that are joint together
as an example of what I mean, my use case:
User needs to either give an input and output filename; this does one file.
Alternatively user gives input and output directories; this does the operation over a predefined subset within those directories.
If one is used, the other shouldn't be
Currently clara::Arg(filenames, "filename"s)("input filenames"s)
does not produce an entry in the help; it's is common for a brief description to appear
usage:
NAME [<filename> ... ] options
where <filename> ... input filenames
where options are:
ETC
or something similar.
Frankly, I also find it strange that the options
appear after the arguments, rather than before, as is fairly standard.
I get a std::bad_alloc
for help strings that are too wide for my console when trying to do std::cerr << cli << std::endl;
The problem comes from this line of code:
auto row =
TextFlow::Column( cols.left ).width( optWidth ).indent( 2 ) +
TextFlow::Spacer(4) +
TextFlow::Column( cols.right ).width( consoleWidth - 7 - optWidth );
where optWidth
is not bounded to be less than consoleWidth - 7
Almost(?) all command line programs will have the --help
option so IMO it would make sense to either support it out of the box or at least make it simpler to implement it than currently as it involves many steps:
bindProcessName()
(it's not going to be used for anything else than the usage message).bool help
to the options struct.-h
and --help
to it.usage()
if help
is true.I'd rather have a way to tell parse()
to do all of this automatically, but this would require changing its return type, as we need to know whether help was requested or not, so maybe a better idea would be to add another method, e.g. parse_or_give_help()
returning bool.
OTOH I think it's quite wrong for parse()
to throw on command line syntax errors, this is not something exceptional and can (and does) happen all the time. So personally I'd rather change parse()
to return an enum indicating whether the command line was successfully parsed, an error occurred or help was requested.
Integer arguments with trailing garbage are successfully decoded instead of giving an error:
42garbage
is decoded as 42 instead of parse error
This will be also a problem for other types. I looked at the implementation and the fix should be quite simple -- just checking if the stringstream is empty after the conversion.
Why would I use this as opposed to:
http://www.boost.org/doc/libs/1_60_0/doc/html/program_options.html
Note that windows syntax for command line options is kinda silly; anything that is platform-agnostic uses -
and --
notations. I'm not sure if Boost Program Options lets you change this syntax, but I never use anything but it.
Maybe your documentation can explain why we need another parser for this, kind of like you do for Catch.
Many programs use a single -
(minus sign) on the command line to mean "read from stdin" or similar. Clara currently doesn't support this, a lonely -
is always reported as error and doesn't end up in the positional arguments.
Composing args with + symbols causes errors on compilation. Those should probably be | symbols instead.
Hi,
I'm writing a little script interpreter using your option parser.
It would be cool if we could specify an arbitrary plain string as argument, like a filename, without specifying '--filename' before.
This would allow to support shebangs because this :
script.custom_extension
#! /bin/custom_interpreter
is read by the kernel and run as :
/bin/custom_interpreter script.custom_extension
Thank you for your option parser, it's very useful for me.
It would be nice to use the newer CMake INTERFACE
option in conjunction with add_library
and the appropriate install
entries in the CMakeLists.txt
.
It would also be nice to add the tests explicitly using add_test
so that ctest
can be run easily.
This test case fails:
TEST_CASE( "Opt value can start with a dash" ) {
std::string name;
bool showHelp = false;
auto parser
= Help( showHelp )
| Opt( name, "name" )
["-n"]["--name"]
( "the name to use" );
auto result = parser.parse( Args{ "TestApp", "-n", "-lfoobar" } );
CHECK( result );
REQUIRE( name == "-lfoobar" );
}
I have a program that receives compiler flags in arguments so a leading dash is common.
I've been trying to work out what license Clara is under. The "single-include" contains license text from clara_textflow.hpp:
// This work is licensed under the BSD 2-Clause license.
// See the accompanying LICENSE file, or the one at https://opensource.org/licenses/BSD-2-Clause
But I can't see a LICENSE file in the repository.
The separate clara.hpp contains no license information at all.
I suspect that Clara is also under the 2-clause BSD license, but I think this needs to be made completely clear.
Thanks.
Mike.
The readme says:
"You can combine parsers by composing with
+
, like this:"
The example uses |
.
I would like to know if the user specified a particular command line option. I can do that by setting a bool in a bound function, but I can't help thinking that the framework already 'knows' if the option got used...I couldn't see any way to find out if it had been set though - can it be done?
My rather cludgy example code;
#define CLARA_CONFIG_MAIN
#include <clara.h>
#include <iostream>
struct Opt
{
Opt()
: hostnameWasSet(false)
{}
std::string processName;
bool hostnameWasSet;
std::string hostname;
void setHostname(const std::string& h)
{
hostnameWasSet = true;
hostname = h;
}
};
int main(int argc, char* argv[])
{
using namespace Clara;
try
{
CommandLine<Opt> cli;
cli.bindProcessName(&Opt::processName);
cli["--host"]
.describe("set the host name")
.bind(&Opt::setHostname, "api.github.com");
Opt opt;
cli.parseInto(argc, const_cast<const char**>(argv), opt);
if (opt.hostnameWasSet)
{
std::cout << "Hostname was set..." << opt.hostname << std::endl;
}
else
{
std::cout << "Hostname was NOT set...do stuff that doesn't need the hostname" << std::endl;
}
}
catch (const std::exception& e)
{
std::cerr << "Exception: " << e.what() << std::endl;
}
catch (...)
{
std::cerr << "Unknown exception..." << std::endl;
}
return 0;
}
This is the result that I want:
c:\Projects\ClaraExample\Debug>ClaraExample.exe --host=fred
Hostname was set...fred
c:\Projects\ClaraExample\Debug>ClaraExample.exe
Hostname was NOT set...do stuff that doesn't need the hostname
I'm a bit confused by the placeholder...what I think I want is to be able to set a default value for that field; I know that I can set it in the options struct constructor but I was expecting to be able to use the placeholder. However, that didn't work if the option never got specified...
So I left the placeholder empty (since it didn't get used) but then when I run it it throws an invalid conversion:
c:\Projects\ClaraExample\Debug>ClaraExample.exe --host
Exception: Invalid conversion
- while parsing: (--host)
c:\Projects\ClaraExample\Debug>ClaraExample.exe --host=fred
Exception: Invalid conversion
- while parsing: (--host)
This is the code:
#define CLARA_CONFIG_MAIN
#include <clara.h>
#include <iostream>
struct Opt
{
Opt()
: showHelp(false)
, hostname("api.github.com")
{}
std::string processName;
bool showHelp;
std::string hostname;
};
int main(int argc, char* argv[])
{
using namespace Clara;
try
{
CommandLine<Opt> cli;
cli.bindProcessName(&Opt::processName);
cli["-?"]["-h"]["--help"]
.describe("describes how to use the program")
.bind(&Opt::showHelp);
cli["--host"]
.describe("host to connect to")
.bind(&Opt::hostname, "");
//.bind(&Opt::hostname, "must not be empty...but never gets used");
Opt opt;
cli.parseInto(argc, const_cast<const char**>(argv), opt);
if (opt.showHelp)
{
cli.usage(std::cout, opt.processName);
}
else
{
std::cout << "Host: " << opt.hostname << std::endl;
}
}
catch (const std::exception& e)
{
std::cerr << "Exception: " << e.what() << std::endl;
}
catch (...)
{
std::cerr << "Unknown exception..." << std::endl;
}
return 0;
}
I know that I can make this work by specifiying the placeholder, but it never seems to get used (using the 'bind' that is commented out above):
# This is fine...option specified on the command line...
c:\Projects\ClaraExample\Debug>ClaraExample.exe --host=fred
Host: fred
# This fails (and doesn't seem to use the placeholder)...
c:\Projects\ClaraExample\Debug>ClaraExample.exe --host
Exception: Expected argument to option: host
How do I make the placeholder useful? Can the value be 'optional'?
Is it possible/desirable to set the bound member variable (perhaps to the value of the placeholder) even if I don't specify the option on the command line (I;m not sure what side effects this might have on existing code...)?
I know that the Arg class is meant to take variadic arguments, but why can't the Opt class take variadic arguments as well? I would like to take multiple arguments for 2 different options, but the Opt class doesn't accept vectors. Is there any reason this couldn't be changed, or is there some implementation obstacle that prevents Opts from taking vectors?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.