This is a LLVM-style lightweight C++ option parser library, supporting the standard GNU style syntax for options.
Options can be given as:
--long
--long=argument
--long argument
-a
-ab
-abc argument
-c5
where c takes an argument, but a and b do not.
Additionally, anything after --
will be parsed as a positional argument.
#include <yaco.hpp>
Create a yaco::Parser
instance or not.
yaco::Parser parser("MyProgram");
Then declare options anywhere.
yaco::Opt<bool> debug("d",
yaco::cat("Analysis"),
yaco::init(false),
yaco::desc("Enable debug"),
yaco::inject(parser)
);
First, you must specify the option storage value type.
yaco::Opt<bool> debug;
Options are declared with a long and an optional short option.
yaco::Opt<bool> debug("d");
If not specified, it will be treated as a position-related option.
You can using a lot of modifier to modify the attributes of option. These modifiers are currently provided:
yaco::cat
yaco::init
yaco::desc
yaco::inject
If you want to add option to the specified parser,
you should use the yaco::inject
modifier.
You can specify a default value through the yaco::init
modifier,
but you may encounter some conversion problems.
Yes, any type can be given as long as it can be parsed, with operator>>.
But there are more types that cannot be parsed, such as vector<string>
.
So, you should DIY the serializer.
For example, to handle the vector<string>
:
namespace yaco
{
template<template<typename...> class C, typename U>
struct val2str <C<U>>
{
static std::string make(std::vector<U> vec)
{
std::string buf;
for (auto iter = vec.begin(), end = vec.end(); iter != end; ++iter)
{
buf.append(val2str<U>::make(*iter)).append(1, CXXOPTS_VECTOR_DELIMITER);
}
if (!buf.empty())
{
buf.pop_back();
}
return buf;
}
};
}
You will encounter some compilation problems when you decide to use a custom structure. This is usually because you didn't tell the compiler how to serialize and deserialize.
Here is a structure:
struct uti {
std::string a = "";
int i = 0;
};
and option:
yaco::Opt<struct uti> utisw("u");
Now, we declare the special value parser:
namespace cxxopts {
namespace values {
template <>
ErrOr<void>
parse_value<uti>(const std::string& text, uti& value) {
value.a = text;
value.i = (int) text.length();
return true;
}
}
}
The parser will call it during the parsing process.
To parse the command line do:
int result = parser.enparse(argc, argv);
To retrieve an option use opt.IsSpecified()
to check if it appeared,
and use the option self to get its value.
If "opt" doesn't exist, or isn't of the right type, then an exception will be thrown.
Exceptional situations throw C++ exceptions. There are two types of
exceptions: errors defining the options, and errors when parsing a list of
arguments. All exceptions derive from cxxopts::OptionException
. Errors
defining options derive from cxxopts::OptionSpecException
and errors
parsing arguments derive from cxxopts::OptionParseException
.
All exceptions define a what()
function to get a printable string
explaining the error.
Options can be placed into groups for the purposes of displaying help messages.
To place options in a group, pass the group as a string to add_options
. Then,
when displaying the help, pass the groups that you would like displayed as a
vector to the help
function.
Positional arguments can be optionally parsed into one or more options. To set up positional arguments, use
yaco::Opt<std::string> head(yaco::value_desc("head file"),
yaco::Positional
);
Compared to cxxopts, yaco support enumerations:
enum OptLevel { Debug, O1, O2, O3 };
yaco::Opt<OptLevel> OptimizationLevel(
"opt",
yaco::desc("Choose optimization level."),
yaco::values(
coEnumValN(Debug, "g", "No optimizations, enable debugging"),
coEnumVal(O1, "Trivial optimizations"),
coEnumVal(O2, "Default optimizations"),
coEnumVal(O3, "Expensive optimizations")
),
yaco::inject(parser)
);
When using the command line, specify it in a way similar to --opt O3
.
And use
std::cout << "optimization level: ";
switch (OptimizationLevel) {
case OptLevel::Debug: std::cout << "debug" << std::endl; break;
case OptLevel::O1: std::cout << "O1" << std::endl; break;
case OptLevel::O2: std::cout << "O2" << std::endl; break;
case OptLevel::O3: std::cout << "O3" << std::endl; break;
}
to check the enumeration value.
Boolean options have a default implicit value of "true"
, which can be
overridden. The effect is that writing -o
by itself will set option o
to
true
. However, they can also be written with various strings using =value
.
There is no way to disambiguate positional arguments from the value following
a boolean, so we have chosen that they will be positional arguments, and
therefore, -o false
does not work.
std::vector<T>
valuesParsing of list of values in form of an std::vector<T>
is also supported, as long as T
can be parsed. To separate single values in a list the definition CXXOPTS_VECTOR_DELIMITER
is used, which is ',' by default. Ensure that you use no whitespaces between values because
those would be interpreted as the next command line option. Example for a command line option
that can be parsed as a std::vector<double>
:
--my_list=1,-2.1,3,4.5
The same option can be specified several times, with different arguments, which will all be recorded in order of appearance. An example:
--use train --use bus --use ferry
this is supported through the use of a vector of value for the option:
yaco::Opt<std::vector<std::string>> used("use",
yaco::desc("multiple used")
);
Putting all together:
#include "yaco.h"
struct uti {
std::string a = "";
int i = 0;
};
/// Type converter
namespace cxxopts {
namespace values {
template <>
ErrOr<void>
parse_value<uti>(const std::string& text, uti& value) {
value.a = text;
value.i = (int) text.length();
return true;
}
}
}
yaco::Opt<bool> debug("d",
yaco::cat("Analysis"),
yaco::init(false),
yaco::desc("Enable debug")
);
yaco::Opt<bool> coverage("c,cover",
yaco::cat("Analysis")
);
yaco::Opt<bool> help("h,help",
yaco::desc("display help messages.\n"
"=== Here is more detail ==="),
yaco::cat("Auxiliary")
);
yaco::Opt<int> job("j,job",
yaco::desc("jobs"),
yaco::value_desc("N")
);
yaco::Opt<struct uti> utisw("u"
);
yaco::Opt<std::string> output("o",
yaco::init("a.out"),
yaco::value_desc("filename")
);
// These are two position-related parameters.
yaco::Opt<std::string> head(yaco::value_desc("head file"), yaco::Positional);
yaco::Opt<std::vector<std::string>> inputs("inputs",
yaco::value_desc("input files"),
yaco::Positional
);
enum OptLevel { Debug, O1, O2, O3 };
yaco::Opt<OptLevel> OptimizationLevel(
"opt",
yaco::desc("Choose optimization level."),
yaco::values(
coEnumValN(Debug, "g", "No optimizations, enable debugging"),
coEnumVal(O1, "Trivial optimizations"),
coEnumVal(O2, "Default optimizations"),
coEnumVal(O3, "Expensive optimizations")
)
);
yaco::Opt<std::vector<std::string>> used("use",
yaco::desc("multiple used")
);
int main(int argc, char **argv)
{
yaco::enparse(argc, argv);
if (job.IsSpecified())
{
int n = job;
std::cout << "Parallel jobs: " << n << std::endl;
}
if (debug)
{
std::cout << "debug on" << std::endl;
}
else
{
std::cout << "debug off" << std::endl;
}
std::cout << "optimization level: ";
switch (OptimizationLevel) {
case OptLevel::Debug: std::cout << "debug" << std::endl; break;
case OptLevel::O1: std::cout << "O1" << std::endl; break;
case OptLevel::O2: std::cout << "O2" << std::endl; break;
case OptLevel::O3: std::cout << "O3" << std::endl; break;
}
if (coverage)
{
std::cout << "coverage on" << std::endl;
}
else
{
std::cout << "coverage off" << std::endl;
}
if (utisw.IsSpecified())
{
std::cout << "util args: " << utisw.a
<< " len=" << utisw.i
<< std::endl;
}
if (head.IsSpecified())
{
std::cout << "head: " << head << std::endl;
}
if (inputs.IsSpecified())
{
std::cout << "inputs: ";
for (auto &input : inputs)
{
std::cout << input << " ";
}
std::cout << std::endl;
}
if (used.IsSpecified())
{
std::cout << "used: ";
for (auto &u : used)
{
std::cout << u << " ";
}
std::cout << std::endl;
}
if (help.IsSpecified())
{
std::cout << yaco::help() << std::endl;
exit(0);
}
return 0;
}
This is a header only library.
The only build requirement is a C++ compiler that supports C++11 features such as:
GCC >= 4.8.5 or clang >= 3.1 with libc++ are known to work.
The following compilers are known not to work:
Thanks to the ubisec company for my work, they allowed me more time to write this library.
Thanks to jarro2783, his cxxopts project provided yaco's original parser, which is currently the core parser of yaco.
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。