This is args-parser.
args-parser is a small C++ header-only library for parsing command line arguments.
- Syntax
- Different types of strings.
- Different types of list of strings.
- Q/A
- Why not to add description, long description, etc. into constructors of arguments, so it will be possible to initialize argument in one line?
- How can I add
args-parserto my project? - Why should I use this library?
- How to print help programmatically?
- Why doesn't
args-parserprovide bindings of variables to arguments? - Why don't you provide comparisons with other CLI libraries?
- Changelog
- Example
- An argument starts with two dashes. For example
--argument. - A flag starts with one dash. For example
-b. - If an argument has a value, then the value can be specified after a space or after an equal sign.
For example
--argument valueand--argument=value. - Flags without values can be combined into one block. For example
-abcdefines three flags-a,-band-c. - The last flag in a flags' block can have a value. For example
-abc=value. And here, flag-cwill have the valuevalue. MultiArgclass provides the ability to define more than one value for an argument. These types of arguments can be specified more than once in the command line. And the resulted value of the argument will beStringList.Commandclass can be used to define command in command line interface. Command is the argument without dash/dashes at the beginning,addfor example.Commandcan has children arguments or even subcommands.- In
args-parsergroups can be used to group arguments into groups to check their definitions after parsing, so if constraint of group will be violated exception will be thrown. args-parserprovidesHelpargument that provides help printing.Helpuses-h, --helparguments.Helpcan receive value with name of argument or command to print help about. IfHelpreceives the name of a command as the value, then the name of a subcommand or a child argument can also be provided as the next value, to get help about the subcommand/child argument.- If
args-parserdon't know about argument in command line interface it provides information about possible arguments if some misspelling was in command line interface. Ifargs-parsercan't assume anything about entered argument it will just say about unknown argument through the exception and parsing will fail. - With
CmdLine::HandlePositionalArgumentsoption provided toCmdLineconstructor,args-parserwill handle positional arguments, these are such arguments that can't be parsed with provided arguments, flags, commands and stay at the end of the command line. Be careful with combiningMultiArgand positional arguments, becauseMultiArgcan eat almost everything that is not a valid argument.
Since version 4.0.0 args-parser can be built with different strings - std::string,
std::wstring and QString.
- To build
args-parserwithstd::wstringsupport defineARGS_WSTRING_BUILD - To build
args-parserwithQStringsupport defineARGS_QSTRING_BUILD - If nothing was defined then
args-parserwill be build withstd::string.
args-parser extensively uses list of string in internal structures and to return
values of arguments outside. In the code this is StringList
type defined in args-parser/types.hpp. By default underlying type is
std::vector or QVector when building with Qt that can be changed
to std::list, QLinkedList or std::deque, QList.
- Define
ARGS_LISTto buildargs-parserwithstd::list,QLinkedListasStringList - Define
ARGS_DEQUEto buildargs-parserwithstd::deque,QListasStringList
Why not to add description, long description, etc. into constructors of arguments, so it will be possible to initialize argument in one line?
- This is impossible because constructors will be ambiguous but you can use auxiliary API that allows to define arguments in one line of code.
-
The simplest way is just copy
args-parserdirectory with headers to any location in your project. With CMake you can clone entireargs-parserproject somewhere in your project and just doadd_subdirectory(), if you will do so you have to add include directory path to your project withinclude_directories( ${args-parser_INCLUDE_DIRECTORIES} ). -
You can clone/download
args-parser, build and install it with CMake. In this case it will be possible to usefind_package( args-parser )inCMakeLists.txtof your project. -
You can use
Conanpackage manager.
-
There are tons of libraries for parsing command line arguments on the Internet. But
args-parseralso provides the possibility to define commands, likegit add -A git commit git pushHelp output is very user-friendly.
If you need only simple arguments in style
--do-it <value>then possibly you will find another library more useful for you, but who knows...
-
For those, who use old style syntax the answer should be on the plate, as developer can look at the code of
Helpclass, that implements help argument. But for those, who prefer one-line syntax the answer can be not so evident, but it's so too. It doesn't matter what syntax you use, new (one-line) syntax is just a wrapper for old one. I.e. line:cmd.addHelp( true, argv[ 0 ], "This application just show power of the Args help." );
Is just a wrapper around of
Helpclass. For printing helpargs-parserusesHelpPrinterclass. So developer can use it for printing help in some rare cases, as:HelpPrinter printer; printer.setExecutable( argv[ 0 ] ); printer.setAppDescription( "This application just show power of the Args help." ); printer.setLineLength( length ); printer.setCmdLine( cmd ); printer.print( outStream() );
- This is a question of why doesn't
args-parserprovide validators? It's the same. I decided that this aspect is very application specific. There is no need for such library to do any conversions of arguments' values, to bind them to some variables. This will do API very complex only. I know what you will say: this is very nice feature, it helps... Really? How often and how much it helped you? Arguments parser should handle the string that user provided to the application, it should separate arguments, commands, values, store it in internal state of parser, and allow to developer just write someifoperators to choose branch of the program logic. What will give you, as to developer, if values will be bind to concrete variables? Will not you write the same code withifoperators? So why I should do the library more complex?
-
I found only one library at GitHub that can compete with
args-parser, and this is CLI11. And here is the question of the taste more. ButCLI11can handle commands as usual arguments, it's doesn't matter how much times they present in command line, whereasargs-parserhandles commands as commands. Theirs approach possibly more flexible, but when I designedargs-parserI thought on commands as on some action to do in application's logic, whereas arguments are for data. I can do the same, but is it needed?CLI11has possibility to set formatter of the help,args-parserallow to set customHelpPrinterIfaceon theHelpargument. But who and when will do it? And I believe that help inargs-parseris a little better than inCLI11.CLI11works more with callbacks, whereasargs-parseruses hierarchy of classes with polymorphism. Inargs-parseryou can inherit from any argument's class, override methods you need and receive something very application specific. And again, this is more question of taste.Uh, oh, I found one more interesting arguments parsing library, This is Taywee/Args. Guys, this is a question of taste. And, as said in
CLI11documentation aboutTaywee/Args,args-parserasCLI11less verbose.Taywee/Argshas benchmark to compare performance withTCLAPandboost::program_options., I want to say thatargs-parser2 times faster thanTaywee/Args, dry numbers says thatTyawee/Argsruns 0.635314 second, whereasargs-parserruns 0.346813 second.What I want to say about minuses of
args-parseris that I don't support other delimiter characters than-for flags and--for arguments.
| Version | Changes |
|---|---|
| 6.3.4 | Fixed issue with MSVC when globally defined ::Command class was detected as friend of ArgIface instead of Args::Command |
| 6.3.3 | Minor fix for compilation with -Werror=shadow |
| 6.3.2 | Fixed multiple definitions when included from different compile units. |
| 6.3.1 | Improved performance. Added possibility to set positional arguments string for the help. Added benchmark. |
| 6.3.0 | Added possibility to handle positional arguments. |
| 6.2.0.1 | Ready for use with Qt6. |
| 6.2.0.0 | Allowed to inherit from some classes. Added addArg() methods into API. |
| 6.1.1.1 | Added possibility to set up custom help printer in help argument. |
| 6.1.1.0 | Improved API with new syntax, now it's impossible to mess with end() methods. Fixed issue with printing help of global argument under command. |
| 6.1.0.0 | Added possibility to add Command into groups. |
| 6.0.0.0 | In this version was removed ArgAsCommand class, and was added fully-featured support for sub-commands, that can be created with Command class, i.e. Command can has Command as child. So it's possible to create such CLI as git submodule update --init --recursive. |
First of all you must know that practically all classes of the args-parser throws exceptions on errors
and there is one specific exceptions that inform you about that that help was shown. This specific
exception (HelpHasBeenPrintedException) is needed for processing program's logic that usually stops
execution at this point.
Since version 5.0.0 args-parser provides two API: the old one and auxiliary API
that allows to define arguments in one line of code. Let's look.
// args-parser include.
#include <args-parser/all.hpp>
using namespace Args;
int main( int argc, char ** argv )
{
try {
CmdLine cmd( argc, argv, CmdLine::CommandIsRequired );
cmd.addCommand( "add", ValueOptions::NoValue, true, "Add file." )
.addCommand( "file", ValueOptions::ManyValues, false, "File name.", "", "", "fn" )
.end()
.end()
.addCommand( "delete", ValueOptions::NoValue, true, "Delete file." )
.addCommand( "file", ValueOptions::ManyValues, false, "File name.", "", "", "fn" )
.end()
.end()
.addHelp( true, argv[ 0 ],
"This application just show power of the args-parser help." );
cmd.parse();
if( cmd.isDefined( "file" ) )
for( const auto & fn : cmd.values( "file" ) )
outStream() << fn << "\n";
}
catch( const HelpHasBeenPrintedException & )
{
return 0;
}
catch( const BaseException & x )
{
outStream() << x.desc() << "\n";
return 1;
}
return 0;
}#include <args-parser/all.hpp>
using namespace Args;
void process( bool b, const std::string & value )
{
outStream() << "Boolean: " << b << " , value: \"" << value << "\"\n";
}
int main( int argc, char ** argv )
{
try {
CmdLine cmd( argc, argv );
cmd.addArgWithFlagAndName( 'b', "bool", false, false, "Boolean flag",
"Boolean flag, used without value" )
.addArgWithFlagAndName( 'v', "value", true, false, "With value",
"Argument with value", "", "VAL" )
.addHelp( true, argv[ 0 ], "CLI with boolean and value." );
cmd.parse();
bool b = false;
std::string value;
if( cmd.isDefined( "-b" ) )
b = true;
if( cmd.isDefined( "-v" ) )
value = cmd.value( "-v" );
process( b, value );
}
catch( const HelpHasBeenPrintedException & )
{
return 0;
}
catch( const BaseException & x )
{
outStream() << x.desc() << "\n";
return 1;
}
return 0;
}// args-parser include.
#include <args-parser/all.hpp>
// C++ include.
#include <iostream>
int main( int argc, char ** argv )
{
try {
/*
We create Args::CmdLine instance for parsing
command line arguments.
*/
Args::CmdLine cmd( argc, argv );
/*
And create our arguments.
*/
/*
This is argument with flag "-o" and name "--host".
He is with value and required.
*/
Args::Arg host( 'o', "host",
// Argument is with value.
true,
// Argument is required.
true );
// Set description of the argument.
host.setDescription( "Host. Can be \"localhost\", \"any\" or regular IP." );
// We can specify long description too.
host.setLongDescription( "Host. This argument told to the application "
"where to open socket for communication." );
Args::Arg port( 'p', "port", true, true );
port.setDescription( "Port number to create socket." );
/*
This argument have name "--timeout" only.
He is with value but optional.
*/
Args::Arg timeout( "timeout", true );
// This argument want to specify value specifier in the help. Let's do it.
timeout.setValueSpecifier( "ms" );
timeout.setDescription( "Timeout before new messages will be sent "
"in milliseconds." );
/*
We create help now.
*/
Args::Help help;
// Set executable name to the help printer.
help.setExecutable( argv[ 0 ] );
// And set description of the application.
help.setAppDescription( "This application just show "
"the power of args-parser." );
/*
Now add our argument to the command line.
*/
cmd.addArg( host );
cmd.addArg( port );
cmd.addArg( timeout );
cmd.addArg( help );
/*
Now parse our arguments.
*/
cmd.parse();
if( timeout.isDefined() )
auto timeoutValue = timeout.value();
}
catch( const Args::HelpHasBeenPrintedException & )
{
return 0;
}
catch( const Args::BaseException & x )
{
std::cout << x.what() << "\n";
return 1;
}
return 0;
}Help output for the example with the old syntax.
This application just show the power of args-parser.
USAGE: sample.help.exe -s, --host <arg> -p, --port <arg> [ -h, --help <arg> ]
[ --timeout <ms> ]
REQUIRED:
-s, --host <arg> Host. Can be "localhost", "any" or regular IP.
-p, --port <arg> Port number to create socket.
OPTIONAL:
-h, --help <arg> Print this help.
--timeout <ms> Timeout before new messages will be sent in milliseconds.That's it. Use it and enjoy it. Good luck.