Commit 2db15139cb34e799aebaadf876f9d96035bc0da9

Authored by Henry Fredrick Schreiner
0 parents

Initial commit of CLI library. A few things working, but not much

CLI.hpp 0 โ†’ 100644
  1 +++ a/CLI.hpp
  1 +#pragma once
  2 +
  3 +#include <string>
  4 +#include <regex>
  5 +#include <iostream>
  6 +#include <functional>
  7 +#include <tuple>
  8 +#include <exception>
  9 +#include <algorithm>
  10 +#include <sstream>
  11 +#include <type_traits>
  12 +
  13 +// This is unreachable outside this file; you should not use Combiner directly
  14 +namespace {
  15 +
  16 +void logit(std::string output) {
  17 + std::cout << output << std::endl;
  18 +}
  19 +
  20 +template <typename T>
  21 +std::string join(const T& v, std::string delim = ",") {
  22 + std::ostringstream s;
  23 + for (const auto& i : v) {
  24 + if (&i != &v[0]) {
  25 + s << delim;
  26 + }
  27 + s << i;
  28 + }
  29 + return s.str();
  30 +}
  31 +
  32 +
  33 +
  34 +
  35 +struct Combiner {
  36 + int num;
  37 + bool positional;
  38 + bool required;
  39 + bool defaulted;
  40 +
  41 + /// Can be or-ed together
  42 + Combiner operator | (Combiner b) const {
  43 + Combiner self;
  44 + self.num = num + b.num;
  45 + self.positional = positional || b.positional;
  46 + self.required = required || b.required;
  47 + self.defaulted = defaulted || b.defaulted;
  48 + return self;
  49 + }
  50 +
  51 + /// Call to give the number of arguments expected on cli
  52 + Combiner operator() (int n) const {
  53 + return Combiner{n, positional, required, defaulted};
  54 + }
  55 + Combiner operator, (Combiner b) const {
  56 + return *this | b;
  57 + }
  58 +};
  59 +
  60 +
  61 +}
  62 +
  63 +namespace CLI {
  64 +
  65 +class BadNameString : public std::runtime_error {
  66 +public:
  67 + BadNameString(std::string name) : runtime_error("Failed to parse: " + name) {};
  68 +};
  69 +
  70 +class CallForHelp : public std::runtime_error {
  71 +public:
  72 + CallForHelp() : runtime_error("Help option passed") {};
  73 +};
  74 +
  75 +class ParseError : public std::runtime_error {
  76 +public:
  77 + ParseError(std::string info="") : runtime_error(info) {};
  78 +};
  79 +
  80 +class OptionAlreadyAdded : public std::runtime_error {
  81 +public:
  82 + OptionAlreadyAdded(std::string name) : runtime_error("Already added:" + name) {};
  83 +};
  84 +
  85 +
  86 +
  87 +const std::regex reg_split{R"regex((?:([a-zA-Z0-9]?)(?:,|$)|^)([a-zA-Z0-9][a-zA-Z0-9_\-]*)?)regex"};
  88 +const std::regex reg_short{R"regex(-([^-])(.*))regex"};
  89 +const std::regex reg_long{R"regex(--([^-^=][^=]*)=?(.*))regex"};
  90 +
  91 +
  92 +std::tuple<std::string, std::string> split(std::string fullname) throw(BadNameString) {
  93 +
  94 + std::smatch match;
  95 + if (std::regex_match(fullname, match, reg_split)) {
  96 + std::string sname = match[1];
  97 + std::string lname = match[2];
  98 + if(sname == "" and lname == "")
  99 + throw BadNameString("EMPTY");
  100 + return std::tuple<std::string, std::string>(sname, lname);
  101 + } else throw BadNameString(fullname);
  102 +}
  103 +
  104 +const Combiner NOTHING {0,false,false,false};
  105 +const Combiner REQUIRED {0,false,true, false};
  106 +const Combiner DEFAULT {0,false,false,true};
  107 +const Combiner POSITIONAL{0,true, false,false};
  108 +const Combiner ARGS {1,false,false,false};
  109 +
  110 +typedef std::vector<std::vector<std::string>> results_t;
  111 +typedef std::function<bool(results_t)> callback_t;
  112 +
  113 +class Option {
  114 +public:
  115 +protected:
  116 + // Config
  117 + std::string sname;
  118 + std::string lname;
  119 + Combiner opts;
  120 + std::string discription;
  121 + callback_t callback;
  122 +
  123 + // Results
  124 + results_t results {};
  125 +
  126 +
  127 +public:
  128 + Option(std::string name, std::string discription = "", Combiner opts=NOTHING, std::function<bool(results_t)> callback=[](results_t){return true;}) throw (BadNameString) :
  129 + opts(opts), discription(discription), callback(callback){
  130 + std::tie(sname, lname) = split(name);
  131 + }
  132 +
  133 + /// Process the callback
  134 + bool run_callback() const {
  135 + return callback(results);
  136 + }
  137 +
  138 + /// Indistinguishible options are equal
  139 + bool operator== (const Option& other) const {
  140 + if(sname=="" && other.sname=="")
  141 + return lname==other.lname;
  142 + else if(lname=="" && other.lname=="")
  143 + return sname==other.sname;
  144 + else
  145 + return sname==other.sname || lname==other.lname;
  146 + }
  147 +
  148 + std::string getName() const {
  149 + if(sname=="")
  150 + return "--" + lname;
  151 + else if (lname=="")
  152 + return "-" + sname;
  153 + else
  154 + return "-" + sname + ", --" + lname;
  155 + }
  156 +
  157 + bool check_sname(const std::string& name) const {
  158 + return name == sname;
  159 + }
  160 +
  161 + bool check_lname(const std::string& name) const {
  162 + return name == lname;
  163 + }
  164 +
  165 + std::string get_sname() const {
  166 + return sname;
  167 + }
  168 +
  169 + std::string get_lname() const {
  170 + return lname;
  171 + }
  172 +
  173 +
  174 + int get_num() const {
  175 + return opts.num;
  176 + }
  177 +
  178 + void add_result(int r, std::string s) {
  179 + results.at(r).push_back(s);
  180 + }
  181 + int get_new() {
  182 + results.emplace_back();
  183 + return results.size() - 1;
  184 + }
  185 + int count() {
  186 + return results.size();
  187 + }
  188 +
  189 + std::string string() const {
  190 + std::string val = "Option: " + getName() + "\n"
  191 + + " " + discription + "\n"
  192 + + " [";
  193 + for(const auto& item : results) {
  194 + if(&item!=&results[0])
  195 + val+="],[";
  196 + val += join(item);
  197 + }
  198 + val += "]";
  199 + return val;
  200 + }
  201 +
  202 +};
  203 +
  204 +/// Creates a command line program, with very few defaults.
  205 +/** To use, create a new Program() instance with argc, argv, and a help discription. The templated
  206 +* add_option methods make it easy to prepare options. Remember to call `.start` before starting your
  207 +* program, so that the options can be evaluated and the help option doesn't accidentally run your program. */
  208 +class App {
  209 +public:
  210 +
  211 +protected:
  212 +
  213 + std::string desc;
  214 + std::vector<Option> options;
  215 + std::vector<std::string> missing_options;
  216 + std::vector<std::string> positionals;
  217 +
  218 +public:
  219 +
  220 +
  221 + /// Create a new program. Pass in the same arguments as main(), along with a help string.
  222 + App(std::string discription)
  223 + : desc(discription) {
  224 + }
  225 +
  226 + /// Add an option, will automatically understand the type for common types.
  227 + /** To use, create a variable with the expected type, and pass it in after the name.
  228 + * After start is called, you can use count to see if the value was passed, and
  229 + * the value will be initialized properly.
  230 + *
  231 + * Program::REQUIRED, Program::DEFAULT, and Program::POSITIONAL are options, and can be `|`
  232 + * together. The positional options take an optional number of arguments.
  233 + *
  234 + * For example,
  235 + *
  236 + * std::string filename
  237 + * program.add_option("filename", filename, "discription of filename");
  238 + */
  239 + void add_option(
  240 + std::string name, ///< The name, long,short
  241 + callback_t callback, ///< The callback
  242 + std::string discription="", ///< Discription string
  243 + Combiner opts=ARGS ///< The options (REQUIRED, DEFAULT, POSITIONAL, ARGS())
  244 + ) {
  245 +
  246 + Option myopt{name, discription, opts, callback};
  247 + if(std::find(std::begin(options), std::end(options), myopt) == std::end(options))
  248 + options.push_back(myopt);
  249 + else
  250 + throw OptionAlreadyAdded(myopt.getName());
  251 +
  252 + }
  253 +
  254 + /// Add option for string
  255 + void add_option(
  256 + std::string name, ///< The name, long,short
  257 + std::string &variable, ///< The variable to set
  258 + std::string discription="", ///< Discription string
  259 + Combiner opts=ARGS ///< The options (REQUIRED, DEFAULT, POSITIONAL, ARGS())
  260 + ) {
  261 +
  262 + CLI::callback_t fun = [&variable](CLI::results_t res){
  263 + if(res.size()!=1) {
  264 + return false;
  265 + }
  266 + if(res[0].size()!=1) {
  267 + return false;
  268 + }
  269 + variable = res[0][0];
  270 + return true;
  271 + };
  272 +
  273 + add_option(name, fun, discription, opts);
  274 + }
  275 +
  276 +
  277 + /// Add option for flag
  278 + void add_flag(
  279 + std::string name, ///< The name, short,long
  280 + std::string discription="" ///< Discription string
  281 + ) {
  282 +
  283 + CLI::callback_t fun = [](CLI::results_t res){
  284 + return true;
  285 + };
  286 +
  287 + add_option(name, fun, discription, NOTHING);
  288 + }
  289 +
  290 + /// Add option for flag
  291 + template<typename T>
  292 + typename std::enable_if<std::is_integral<T>::value, void>::type
  293 + add_flag(
  294 + std::string name, ///< The name, short,long
  295 + T &count, ///< A varaible holding the count
  296 + std::string discription="" ///< Discription string
  297 + ) {
  298 +
  299 + count = 0;
  300 + CLI::callback_t fun = [&count](CLI::results_t res){
  301 + count = res.size();
  302 + return true;
  303 + };
  304 +
  305 + add_option(name, fun, discription, NOTHING);
  306 + }
  307 +
  308 +
  309 + /// Parses the command line - throws errors
  310 + void parse(int argc, char **argv) throw(CallForHelp, ParseError) {
  311 + std::vector<std::string> args;
  312 + for(int i=1; i<argc; i++)
  313 + args.emplace_back(argv[i]);
  314 + parse(args);
  315 + }
  316 +
  317 + void parse(std::vector<std::string> args) throw(CallForHelp, ParseError) {
  318 + std::reverse(args.begin(), args.end());
  319 +
  320 + bool positional_only = false;
  321 +
  322 + while(args.size()>0) {
  323 +
  324 + if(args.back() == "--") {
  325 + args.pop_back();
  326 + positional_only = true;
  327 + } else if(positional_only || (!_parse_long(args) && !_parse_short(args))) {
  328 +
  329 + logit("Positional: "+args.back());
  330 + positionals.push_back(args.back());
  331 + args.pop_back();
  332 + }
  333 + }
  334 +
  335 + for(Option& opt : options)
  336 + if (opt.count() > 0) {
  337 + logit(opt.string());
  338 + if(!opt.run_callback())
  339 + logit("Failed");
  340 + }
  341 +
  342 + //TODO: Check for false callbacks
  343 + }
  344 +
  345 + bool _parse_short(std::vector<std::string> &args) {
  346 + std::string current = args.back();
  347 + std::smatch match;
  348 +
  349 + if(!std::regex_match(current, match, reg_short))
  350 + return false;
  351 +
  352 + args.pop_back();
  353 + std::string name = match[1];
  354 + std::string rest = match[2];
  355 +
  356 + logit("Working on short:");
  357 + logit(name);
  358 + logit(rest);
  359 +
  360 + auto op = std::find_if(std::begin(options), std::end(options), [name](const Option &v){return v.check_sname(name);});
  361 +
  362 + if(op == std::end(options)) {
  363 + missing_options.push_back("-" + op->get_sname());
  364 + return true;
  365 + }
  366 +
  367 + int vnum = op->get_new();
  368 + int num = op->get_num();
  369 +
  370 +
  371 + if(rest != "" && num > 0) {
  372 + num--;
  373 + op->add_result(vnum, rest);
  374 + rest = "";
  375 + }
  376 +
  377 + while(num>0) {
  378 + num--;
  379 + std::string current = args.back();
  380 + logit("Adding: "+current);
  381 + args.pop_back();
  382 + op->add_result(vnum,current);
  383 + if(args.size()==0)
  384 + return true;
  385 + }
  386 +
  387 + if(rest != "") {
  388 + rest = "-" + rest;
  389 + args.push_back(rest);
  390 + }
  391 + return true;
  392 + }
  393 +
  394 + bool _parse_long(std::vector<std::string> &args) {
  395 + std::string current = args.back();
  396 + std::smatch match;
  397 +
  398 + if(!std::regex_match(current, match, reg_long))
  399 + return false;
  400 +
  401 + args.pop_back();
  402 + std::string name = match[1];
  403 + std::string value = match[2];
  404 +
  405 +
  406 + logit("Working on long:");
  407 + logit(name);
  408 + logit(value);
  409 +
  410 + auto op = std::find_if(std::begin(options), std::end(options), [name](const Option &v){return v.check_lname(name);});
  411 +
  412 + if(op == std::end(options)) {
  413 + missing_options.push_back("--" + op->get_lname());
  414 + return true;
  415 + }
  416 +
  417 +
  418 + int vnum = op->get_new();
  419 + int num = op->get_num();
  420 +
  421 +
  422 + if(value != "") {
  423 + num--;
  424 + op->add_result(vnum, value);
  425 + }
  426 +
  427 + while(num>0) {
  428 + num--;
  429 + std::string current = args.back();
  430 + args.pop_back();
  431 + op->add_result(vnum,current);
  432 + if(args.size()==0)
  433 + return true;
  434 + }
  435 + return true;
  436 + }
  437 +
  438 + /// This must be called after the options are in but before the rest of the program.
  439 + /** Instead of throwing erros, causes the program to exit
  440 + * if -h or an invalid option is passed. */
  441 + void start(int argc, char** argv) {
  442 + try {
  443 + parse(argc, argv);
  444 + } catch(const CallForHelp &e) {
  445 + std::cout << help() << std::endl;
  446 + exit(0);
  447 + } catch(const ParseError &e) {
  448 + std::cerr << "ERROR:" << std::endl;
  449 + std::cerr << e.what() << std::endl;
  450 + std::cerr << help() << std::endl;
  451 + exit(1);
  452 + }
  453 +
  454 + }
  455 +
  456 + /// Counts the number of times the given option was passed.
  457 + int count(std::string name) const {
  458 + return 0;
  459 + }
  460 +
  461 + std::string help() const {return "";}
  462 +
  463 +
  464 +};
  465 +}
... ...
Program.hpp 0 โ†’ 100644
  1 +++ a/Program.hpp
  1 +#pragma once
  2 +
  3 +#include <string>
  4 +
  5 +#include <boost/program_options.hpp>
  6 +
  7 +
  8 +// This is unreachable outside this file; you should not use Combiner directly
  9 +namespace {
  10 +
  11 +struct Combiner {
  12 + int positional;
  13 + bool required;
  14 + bool defaulted;
  15 +
  16 + /// Can be or-ed together
  17 + Combiner operator | (Combiner b) const {
  18 + Combiner self;
  19 + self.positional = positional + b.positional;
  20 + self.required = required || b.required;
  21 + self.defaulted = defaulted || b.defaulted;
  22 + return self;
  23 + }
  24 +
  25 + /// Call to give the number of arguments expected on cli
  26 + Combiner operator() (int n) const {
  27 + return Combiner{n, required, defaulted};
  28 + }
  29 + Combiner operator, (Combiner b) const {
  30 + return *this | b;
  31 + }
  32 +};
  33 +}
  34 +
  35 +
  36 +
  37 +/// Creates a command line program, with very few defaults.
  38 +/** To use, create a new Program() instance with argc, argv, and a help description. The templated
  39 +* add_option methods make it easy to prepare options. Remember to call `.start` before starting your
  40 +* program, so that the options can be evaluated and the help option doesn't accidentally run your program. */
  41 +class Program {
  42 +public:
  43 + static constexpr Combiner REQUIRED{0,true,false};
  44 + static constexpr Combiner DEFAULT{0,false,true};
  45 + static constexpr Combiner POSITIONAL{1,false,false};
  46 +
  47 +protected:
  48 + boost::program_options::options_description desc;
  49 + boost::program_options::positional_options_description p;
  50 + boost::program_options::variables_map vm;
  51 +
  52 + int argc;
  53 + char **argv;
  54 +
  55 + /// Parses the command line (internal function)
  56 + void parse() {
  57 + try {
  58 + boost::program_options::store(boost::program_options::command_line_parser(argc, argv)
  59 + .options(desc).positional(p).run(), vm);
  60 +
  61 + if(vm.count("help")){
  62 + std::cout << desc;
  63 + exit(0);
  64 + }
  65 +
  66 + boost::program_options::notify(vm);
  67 + } catch(const boost::program_options::error& e) {
  68 + std::cerr << "ERROR: " << e.what() << std::endl << std::endl;
  69 + std::cerr << desc << std::endl;
  70 + exit(1);
  71 + }
  72 + }
  73 +
  74 +
  75 +public:
  76 +
  77 + /// Create a new program. Pass in the same arguments as main(), along with a help string.
  78 + Program(int argc, char** argv, std::string discription)
  79 + : argc(argc), argv(argv), desc(discription) {
  80 + desc.add_options()
  81 + ("help,h", "Display this help message");
  82 + }
  83 +
  84 + /// Allows you to manually add options in the boost style.
  85 + /** Usually the specialized methods are easier, but this remains for people used to Boost and for
  86 + * unusual situations. */
  87 + boost::program_options::options_description_easy_init add_options() {
  88 + return desc.add_options();
  89 + }
  90 +
  91 + /// Add an option, will automatically understand the type for common types.
  92 + /** To use, create a variable with the expected type, and pass it in after the name.
  93 + * After start is called, you can use count to see if the value was passed, and
  94 + * the value will be initialized properly.
  95 + *
  96 + * Program::REQUIRED, Program::DEFAULT, and Program::POSITIONAL are options, and can be `|`
  97 + * together. The positional options take an optional number of arguments.
  98 + *
  99 + * For example,
  100 + *
  101 + * std::string filename
  102 + * program.add_option("filename", filename, "description of filename");
  103 + */
  104 + template<typename T>
  105 + void add_option(
  106 + std::string name, ///< The name, long,short
  107 + T &value, ///< The value
  108 + std::string description, ///< Discription string
  109 + Combiner options ///< The options (REQUIRED, DEFAULT, POSITIONAL)
  110 + ) {
  111 + auto po_value = boost::program_options::value<T>(&value);
  112 + if(options.defaulted)
  113 + po_value = po_value->default_value(value);
  114 + if(options.required)
  115 + po_value = po_value->required();
  116 + desc.add_options()(name.c_str(),po_value,description.c_str());
  117 + if(options.positional!=0)
  118 + p.add(name.c_str(), options.positional);
  119 + }
  120 +
  121 + /// Adds a flag style option
  122 + void add_option(std::string name, std::string description) {
  123 + desc.add_options()(name.c_str(),description.c_str());
  124 + }
  125 +
  126 +
  127 + /// This must be called after the options are in but before the rest of the program.
  128 + /** Calls the Boost boost::program_options initialization, causing the program to exit
  129 + * if -h or an invalid option is passed. */
  130 + void start() {
  131 + parse();
  132 + }
  133 +
  134 + /// Counts the number of times the given option was passed.
  135 + int count(std::string name) const {
  136 + return vm.count(name.c_str());
  137 + }
  138 +
  139 +
  140 +};
... ...
ProgramOP.hpp 0 โ†’ 100644
  1 +++ a/ProgramOP.hpp
  1 +#pragma once
  2 +
  3 +#include <string>
  4 +#include <functional>
  5 +#include <unordered_set>
  6 +#include <vector>
  7 +#include <iostream>
  8 +
  9 +#include "optionparser.h"
  10 +
  11 +// This is unreachable outside this file; you should not use Combiner directly
  12 +namespace {
  13 +
  14 +struct Combiner {
  15 + int positional;
  16 + bool required;
  17 + bool defaulted;
  18 +
  19 + /// Can be or-ed together
  20 + Combiner operator | (Combiner b) const {
  21 + Combiner self;
  22 + self.positional = positional + b.positional;
  23 + self.required = required || b.required;
  24 + self.defaulted = defaulted || b.defaulted;
  25 + return self;
  26 + }
  27 +
  28 + /// Call to give the number of arguments expected on cli
  29 + Combiner operator() (int n) const {
  30 + return Combiner{n, required, defaulted};
  31 + }
  32 +};
  33 +}
  34 +
  35 +/// Creates a command line program, with very few defaults.
  36 +/** To use, create a new Program() instance with argc, argv, and a help description. The templated
  37 +* add_option methods make it easy to prepare options. Remember to call `.start` before starting your
  38 +* program, so that the options can be evaluated and the help option doesn't accidentally run your program. */
  39 +class Program {
  40 +public:
  41 + static constexpr Combiner REQUIRED{0,true,false};
  42 + static constexpr Combiner DEFAULT{0,false,true};
  43 + static constexpr Combiner POSITIONAL{1,false,false};
  44 +
  45 +protected:
  46 + std::vector<option::Descriptor> usage;
  47 + std::vector<std::function<bool(std::vector<std::string>)>> convert; /// Number is loc+2
  48 + std::unordered_set<int> required;
  49 + std::vector<int> counts;
  50 + std::vector<std::string> random_name_store;
  51 +
  52 + int argc;
  53 + char **argv;
  54 +
  55 + /// Parses the command line (internal function)
  56 + void parse() {
  57 + usage.push_back(option::Descriptor{0, 0, nullptr, nullptr, nullptr, nullptr});
  58 +
  59 + option::Stats stats(usage.data(), argc, argv);
  60 + std::vector<option::Option> options(stats.options_max);
  61 + std::vector<option::Option> buffer(stats.buffer_max);
  62 + option::Parser parse(usage.data(), argc, argv, options.data(), buffer.data());
  63 +
  64 + if(parse.error()) {
  65 + std::cerr << "ERROR. See usage:" << std::endl;
  66 + option::printUsage(std::cerr, usage.data());
  67 + exit(1);
  68 + }
  69 +
  70 +
  71 + if(options[1]){
  72 + option::printUsage(std::cerr, usage.data());
  73 + exit(0);
  74 + }
  75 +
  76 + bool found_unk = false;
  77 + for (option::Option* opt = options[0]; opt; opt = opt->next()) {
  78 + std::cout << "Unknown option: " << opt->name << "\n";
  79 + found_unk = true;
  80 + }
  81 + if(found_unk)
  82 + exit(2);
  83 +
  84 + for(int i=2; i<convert.size()+2; i++) {
  85 + counts.emplace_back(options[i].count());
  86 + if(options[i]) {
  87 + std::vector<std::string> opt_list;
  88 + for(option::Option* opt = options[i]; opt; opt = opt->next())
  89 + opt_list.emplace_back(opt->arg ? opt->arg : "");
  90 + convert.at(i-2)(opt_list);
  91 + }
  92 + }
  93 + }
  94 +
  95 +public:
  96 +
  97 + /// Create a new program. Pass in the same arguments as main(), along with a help string.
  98 + Program(int argc, char** argv, std::string description)
  99 + : argc(argc), argv(argv) {
  100 + random_name_store.emplace_back(description);
  101 + usage.push_back(option::Descriptor{0, 0, "", "", option::Arg::None, random_name_store.back().c_str()});
  102 + usage.push_back(option::Descriptor{1, 0, "h", "help", option::Arg::None, "Display usage and exit."});
  103 + }
  104 +
  105 +
  106 + /// Add an option, will automatically understand the type for common types.
  107 + /** To use, create a variable with the expected type, and pass it in after the name.
  108 + * After start is called, you can use count to see if the value was passed, and
  109 + * the value will be initialized properly.
  110 + *
  111 + * Program::REQUIRED, Program::DEFAULT, and Program::POSITIONAL are options, and can be `|`
  112 + * together. The positional options take an optional number of arguments.
  113 + *
  114 + * For example,
  115 + *
  116 + * std::string filename
  117 + * program.add_option("filename", filename, "description of filename");
  118 + */
  119 + template<typename T>
  120 + void add_option(
  121 + std::string name, ///< The name, long,short
  122 + T &value, ///< The value
  123 + std::string description, ///< Description string
  124 + Combiner options ///< The options (REQUIRED, DEFAULT, POSITIONAL)
  125 + ) {
  126 +
  127 + int curr_num = convert.size();
  128 +
  129 + if(options.required)
  130 + required.emplace(curr_num);
  131 +
  132 + random_name_store.emplace_back(name);
  133 + random_name_store.emplace_back(description);
  134 +
  135 + usage.push_back(option::Descriptor{(unsigned int) convert.size()+2, 0, "", random_name_store.at(random_name_store.size()-2).c_str(), option::Arg::Optional, random_name_store.back().c_str()});
  136 + add_option_internal(value);
  137 +
  138 + if(options.positional!=0)
  139 + std::cout << "positional args not yet supported" << std::endl;
  140 +
  141 +
  142 +
  143 + }
  144 +
  145 + /// Adds a flag style option
  146 + void add_flag(std::string name, std::string description, int& flag) {
  147 + counts.emplace_back(0);
  148 + random_name_store.emplace_back(name);
  149 + random_name_store.emplace_back(description);
  150 + usage.push_back(option::Descriptor{(unsigned int) convert.size()+2, 0, "", random_name_store.at(random_name_store.size()-2).c_str(), option::Arg::None, random_name_store.back().c_str()});
  151 + convert.push_back([&flag](std::vector<std::string> v){flag = v.size(); return true;});
  152 + }
  153 +
  154 + void add_option_internal(int &val) {
  155 + convert.push_back([&val](std::vector<std::string> v){val = std::stoi(v.at(0)); return v.size()==1;});
  156 + }
  157 +
  158 + void add_option_internal(std::string &val) {
  159 + convert.push_back([&val](std::vector<std::string> v){val = v.at(0); return v.size()==1;});
  160 + }
  161 + /// This must be called after the options are in but before the rest of the program.
  162 + /** Calls the Boost boost::program_options initialization, causing the program to exit
  163 + * if -h or an invalid option is passed. */
  164 + void start() {
  165 + parse();
  166 + }
  167 +
  168 + /// Counts the number of times the given option was passed.
  169 + int count(std::string name) const {
  170 + return 0;
  171 + }
  172 +
  173 +
  174 +};
... ...
try.cpp 0 โ†’ 100644
  1 +++ a/try.cpp
  1 +#include "CLI.hpp"
  2 +
  3 +
  4 +int main (int argc, char** argv) {
  5 +
  6 + CLI::App app("K3Pi goofit fitter");
  7 +
  8 + std::string file;
  9 + app.add_option("f,file", file, "File name");
  10 +
  11 + int count;
  12 + app.add_flag<int>("c,count", count, "File name");
  13 +
  14 + app.parse(argc, argv);
  15 +
  16 + std::cout << "Working on file: " << file << std::endl;
  17 + std::cout << "Working on count: " << count << std::endl;
  18 +
  19 + return 0;
  20 +}
... ...
try1.cpp 0 โ†’ 100644
  1 +++ a/try1.cpp
  1 +#include "CLI.hpp"
  2 +
  3 +
  4 +int main (int argc, char** argv) {
  5 +
  6 + std::vector<std::string> test_strings = {"a,boo", ",coo", "d,", "Q,this-is", "s", "single"};
  7 +
  8 + for(std::string name : test_strings) {
  9 + std::string one;
  10 + std::string two;
  11 +
  12 + std::tie(one, two) = CLI::split(name);
  13 + std::cout << one << ", " << two << std::endl;
  14 + }
  15 +
  16 + std::vector<std::string> test_fails= {"a,,boo", "a,b,c", "ssd,sfd", "-a", "", ",", "one two"};
  17 +
  18 + for(std::string name : test_fails) {
  19 + std::string one;
  20 + std::string two;
  21 +
  22 + try {
  23 + std::tie(one, two) = CLI::split(name);
  24 + std::cout << "Failed to catch: " << name << std::endl;
  25 + return 1;
  26 + } catch (const CLI::BadNameString &e) {
  27 + std::cout << "Hooray! Caught: " << name << std::endl;
  28 + }
  29 + }
  30 +
  31 +
  32 +}
... ...