# yaco **Repository Path**: RonxBulld/yaco ## Basic Information - **Project Name**: yaco - **Description**: Yaco - Yet Another C++ commandline Option library. - **Primary Language**: C++ - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2021-04-26 - **Last Updated**: 2022-12-13 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README [![Build Status](https://travis-ci.com/RonxBulld/yaco.svg?branch=master)](https://travis-ci.com/RonxBulld/yaco) # Quick start 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. ## Basics ```cpp #include ``` Create a `yaco::Parser` instance or not. ```cpp yaco::Parser parser("MyProgram"); ``` Then declare options anywhere. ```cpp yaco::Opt debug("d", yaco::cat("Analysis"), yaco::init(false), yaco::desc("Enable debug"), yaco::inject(parser) ); ``` First, you must specify the option storage value type. ```c++ yaco::Opt debug; ``` Options are declared with a long and an optional short option. ``` yaco::Opt 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`. So, you should DIY the serializer. For example, to handle the `vector`: ```c++ namespace yaco { template class C, typename U> struct val2str > { static std::string make(std::vector vec) { std::string buf; for (auto iter = vec.begin(), end = vec.end(); iter != end; ++iter) { buf.append(val2str::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: ```c++ struct uti { std::string a = ""; int i = 0; }; ``` and option: ```c++ yaco::Opt utisw("u"); ``` Now, we declare the special value parser: ```c++ namespace cxxopts { namespace values { template <> ErrOr parse_value(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: ```c++ 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. ## Exceptions 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. ## Help groups 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 Positional arguments can be optionally parsed into one or more options. To set up positional arguments, use ```cpp yaco::Opt head(yaco::value_desc("head file"), yaco::Positional ); ``` ## Enumeration Compared to cxxopts, yaco support enumerations: ```c++ enum OptLevel { Debug, O1, O2, O3 }; yaco::Opt 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 ```c++ 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 values 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` values Parsing of list of values in form of an `std::vector` 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`: ~~~ --my_list=1,-2.1,3,4.5 ~~~ ## Options specified multiple times 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> used("use", yaco::desc("multiple used") ); ~~~ ## Example Putting all together: ```cpp #include "yaco.h" struct uti { std::string a = ""; int i = 0; }; /// Type converter namespace cxxopts { namespace values { template <> ErrOr parse_value(const std::string& text, uti& value) { value.a = text; value.i = (int) text.length(); return true; } } } yaco::Opt debug("d", yaco::cat("Analysis"), yaco::init(false), yaco::desc("Enable debug") ); yaco::Opt coverage("c,cover", yaco::cat("Analysis") ); yaco::Opt help("h,help", yaco::desc("display help messages.\n" "=== Here is more detail ==="), yaco::cat("Auxiliary") ); yaco::Opt job("j,job", yaco::desc("jobs"), yaco::value_desc("N") ); yaco::Opt utisw("u" ); yaco::Opt output("o", yaco::init("a.out"), yaco::value_desc("filename") ); // These are two position-related parameters. yaco::Opt head(yaco::value_desc("head file"), yaco::Positional); yaco::Opt> inputs("inputs", yaco::value_desc("input files"), yaco::Positional ); enum OptLevel { Debug, O1, O2, O3 }; yaco::Opt 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> 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; } ``` # Linking This is a header only library. # Requirements The only build requirement is a C++ compiler that supports C++11 features such as: * regex (When using gcc4.8, the regex library is not used.) * constexpr * default constructors GCC >= 4.8.5 or clang >= 3.1 with libc++ are known to work. The following compilers are known not to work: * MSVC 2013 # Thanks 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.