Yet Another Command Line Argument Parser, for c++, as single-header-only library, natively supporting wchar_t, with a minimal feature set, e.g. without advanced type conversion and validation, just because...
This command line parser is inspired by the POSIX standard and especially the System.CommandLine CSharp Library.
Features
Commands also called 'verbs', switching to specific sets of Options, Arguments, and Commands supportedOptions are optionally named input, followed by a Argument valueAliasnames for Options, Switches, and CommandsArguments are values, either following an Option, or being identified by it's position in the stream of arguments, which are not otherwise matched. Arguments have names for the readability of the generated documentation only.Switches are special Options without an explicit Argument--the double-dash token stops the parsing and will return all following arguments as unmatched- Automatic
--helpdocumentation generation is supported - Option-argument delimiters are supported:
(space),=, and: - Supports case-sensitive and case-insensitive matching of names of Options and Switches
- Supports basic value conversion of Option values and Argument values to basic types: long integer numbers, double-precision floating-point numbers, and boolean values
Not Supported
- Input value validation, e.g. integer range check or string to keyword matching, is not supported by this library
- Complex value type conversion and validation is not supported by this library
- Fetch arguments as strings and convert and validate the input with you own code.
- There are utility functions to inject error messages into the parser output.
- Option prefixes,
-,--, and/, are part of the Option's name, and have no further special meaning - Automatic aliasing, e.g. shortening option names, is not supported
- Option bunding (e.g.
-f -d -x=-fdx) is not supported
For a extensive usage example, see src/cmdargs.cpp.
It's recommended to separate the command line arguments parsing code from the rest of your application logic, as seen in this example.
The struct Config defined in src/cmdargs.h publishes the parsed configuration to your application.
All implementation details and dependencies to yaclap are isolated in src/cmdargs.cpp.
Instantiate a Parser object with your application executable name and a description.
This information will be used for the automatically generated usage help.
The Parser object will be your entry point for defining the syntax of the command line arguments and for performing the actual parsing operation.
Options and Switches are named structures of your command line argument syntax.
Options are followed by one value argument.
Switches have no explicit argument.
As with most named structures, Options and Switches can have additional alias names, e.g. a shorter version if they are used very often.
Examples:
Option inputOption{
{_T("--input"), StringCompare::CaseInsensitive},
_T("file"),
_T("An input file")};
inputOption
.AddAlias(_T("-i"))
.AddAlias(_T("/i"));Call as:
your.exe --input c:\path\to\file.txtyour.exe -i c:\path\to\file.txtyour.exe /i c:\path\to\file.txt
Switch verboseSwitch{
{_T("--verbose"), StringCompare::CaseInsensitive},
_T("Verbosity switch")};
verboseSwitch
.AddAlias(_T("-v"))
.AddAlias(_T("/v"));Call as:
your.exe --verboseyour.exe -vyour.exe /v
Arguments are named command line arguments.
They are purely defined by their order or appareance when specified.
Example:
Argument andArgument{
_T("string"),
_T("An additional string argument")};Options and Switches, which are prefixed by their names, have priority over matching an argument.
If there are more command line arguments specified when calling your application than the syntax structure defines, those are so called unnamed arguments.
By default, unnamed arguments will set an error.
Commands can structure your command line argument syntax into groups.
Think of it as mode-switches for your application.
Matching Command names during parsing takes priority over matching Options, Switches, and Arguments.
When specifying your command line argument syntax, you can add Options, Switches, Arguments, and Commands either to the Parser object, or to Command objects.
The parser will start matching the command line against the objects added to the Parser object.
As soon as an argument matches a name of a Command object, the parser "switches modes" to this command:
- It will now match
Commands added to thisCommandobject, and no longerCommands added to theParserobject. - It will now match
Options,Switches, andArguments which are either added to theParserobject or theCommandobject.
Example:
Command commandA{
{_T("CommandA"), StringCompare::CaseInsensitive},
_T("Command A")};
commandA
.AddAlias({_T("CmdA"), StringCompare::CaseInsensitive})
.AddAlias(_T("A"))
.Add(inputOption);parser
.Add(commandA)
.Add(verboseSwitch);Call as:
your.exe -v-- works, as theverboseSwitchis added to theParser.your.exe CmdA -i c:\path\file.txt-- TheinputOptionis only available after theParserswitched to the "mode" ofCommand commandA.your.exe CmdA -v-- works, because the verbose switch remains available event after switching to the "mode" ofCommand commandA.
The Parser::Result only structures the input command line, e.g. selects the input strings based on the defined syntax:
Parser::Result res = parser.Parse(argc, argv);Elements without values, i.e. Commands and Switches, are queried if they were present in the command line or how often they were present in the command line:
if (res.HasCommand(commandA))m_verbose = static_cast<int>(res.HasSwitch(verboseSwitch));Elements with values, i.e. Options and Arguments, are queried either by receiving the first occurance, optionally setting and error if the Option occured multiple times in the command line, or by receiving all occurances:
auto inputValue = res.GetOptionValue(inputOption, Parser::Result::ErrorIfMultiple);auto andValue = res.GetArgument(andArgument);for (Parser::ResultValueView const& s : res.GetOptionValues(intValueOption))The returned Parser::ResultValueView objects are string_views to the original input strings.
These objects can also be queried via IsFromSource if they were created from matching a specific element, e.g. Option or named Argument, and at which position in the original input argument list they were.
Additionally, the Parser::ResultValueView offers three utility functions to convert the value from a raw string to other data types:
std::optional<long long> Parser::ResultValueView::AsInteger(...)std::optional<double> Parser::ResultValueView::AsDouble(...)std::optional<bool> Parser::ResultValueView::AsBool(...)
See the code documentation of these functions for details on the accepted input syntax.
These utility functions allow for easy assignments of such values to the configruation struct:
auto dValOpt = res.GetOptionValue(doubleValueOption, Parser::Result::ErrorIfMultiple).AsDouble();
if (dValOpt)
{
m_doubleValue = dValOpt.value();
}or
m_boolValue = res.GetOptionValue(boolValueOption, Parser::Result::ErrorIfMultiple).AsBool().value_or(false);The Parser::Result object also holds a general success flag and an error message if one was set.
It is recommended to show the error message and possibly usage help information automatically:
parser.PrintErrorAndHelpIfNeeded(res);And, you should inform the your application's main logic whether or not the command line argument parsing function was successful:
return res.IsSuccess() && !res.ShouldShowHelp();Note: this example code returns a none-success value if the internal automatic help switch was invoked by the user.
The help text was already printed by PrintErrorAndHelpIfNeeded above.
So, returning false here avoids further program execution.
You can, of course, implement other means to stop the program after showing the help text, to distinguish this operation from error cases.
You can either use the Nuget package (recommended) or directly add the source code.
Add the Nuget package SGrottel.yaclap to your project.
The include path will automatically be set accordingly, so you can
#include "yaclap.hpp"Alternatively you can just copy the contents of a release zip or the contents of the include directory to your project (not recommended). It's recommended you place the files into a separate subdirectory and add that directory to your include directories compiler options, for separation of concerns.
If you found an issue, please have a look at the Issues list.
If it is an already existing issue, feel free to up-vote it. If you can contribute additional information, e.g. further reproduction steps, please comment on the issue.
If you do not find a matching issue, please create one, with all steps necessary to reproduce the issue, and all further information you can provide.
If you already have a fix ready, please, create and issue and a linked Pull request.
Thank you!
Contributions are very welcome!
Please, create Issues and Pull requests.
Please, keep in mind, that one of the major goals of the project is to create a minimalistic library. Many sophisticated functionality might get rejected, if it would wide the scope of the library too much.
In doubt, please reach out to me, via message, e-mail or Issue.
Thank you!
There are plenty of alternatives. Especially, if your project is already using a larger framework, you likely already have something available to you.
- Qt
QCommandLineParser(https://doc.qt.io/qt-6/qcommandlineparser.html) Boost.Program_options(https://www.boost.org/doc/libs/1_57_0/doc/html/program_options.html)- GNU
getopt - Google
gflags - ...
- Finding alternative cpp command line argument parsers
The "minimalistic" and the "native wchar_t support" features are the reasons I started this library.
This library is freely available as open source under the MIT License:
MIT License
Copyright (c) 2024 Sebastian Grottel
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
If you are using this library in an open-source software, the above copyright notice must not be removed from the yaclap.hpp file.
But you do not need to reproduce the copyright notice in the compiled binary or accompanying documentation.