#pragma once #include #include #include #include #include #include "optionparser.h" // This is unreachable outside this file; you should not use Combiner directly namespace { struct Combiner { int positional; bool required; bool defaulted; /// Can be or-ed together Combiner operator | (Combiner b) const { Combiner self; self.positional = positional + b.positional; self.required = required || b.required; self.defaulted = defaulted || b.defaulted; return self; } /// Call to give the number of arguments expected on cli Combiner operator() (int n) const { return Combiner{n, required, defaulted}; } }; } /// Creates a command line program, with very few defaults. /** To use, create a new Program() instance with argc, argv, and a help description. The templated * add_option methods make it easy to prepare options. Remember to call `.start` before starting your * program, so that the options can be evaluated and the help option doesn't accidentally run your program. */ class Program { public: static constexpr Combiner REQUIRED{0,true,false}; static constexpr Combiner DEFAULT{0,false,true}; static constexpr Combiner POSITIONAL{1,false,false}; protected: std::vector usage; std::vector)>> convert; /// Number is loc+2 std::unordered_set required; std::vector counts; std::vector random_name_store; int argc; char **argv; /// Parses the command line (internal function) void parse() { usage.push_back(option::Descriptor{0, 0, nullptr, nullptr, nullptr, nullptr}); option::Stats stats(usage.data(), argc, argv); std::vector options(stats.options_max); std::vector buffer(stats.buffer_max); option::Parser parse(usage.data(), argc, argv, options.data(), buffer.data()); if(parse.error()) { std::cerr << "ERROR. See usage:" << std::endl; option::printUsage(std::cerr, usage.data()); exit(1); } if(options[1]){ option::printUsage(std::cerr, usage.data()); exit(0); } bool found_unk = false; for (option::Option* opt = options[0]; opt; opt = opt->next()) { std::cout << "Unknown option: " << opt->name << "\n"; found_unk = true; } if(found_unk) exit(2); for(int i=2; i opt_list; for(option::Option* opt = options[i]; opt; opt = opt->next()) opt_list.emplace_back(opt->arg ? opt->arg : ""); convert.at(i-2)(opt_list); } } } public: /// Create a new program. Pass in the same arguments as main(), along with a help string. Program(int argc, char** argv, std::string description) : argc(argc), argv(argv) { random_name_store.emplace_back(description); usage.push_back(option::Descriptor{0, 0, "", "", option::Arg::None, random_name_store.back().c_str()}); usage.push_back(option::Descriptor{1, 0, "h", "help", option::Arg::None, "Display usage and exit."}); } /// Add an option, will automatically understand the type for common types. /** To use, create a variable with the expected type, and pass it in after the name. * After start is called, you can use count to see if the value was passed, and * the value will be initialized properly. * * Program::REQUIRED, Program::DEFAULT, and Program::POSITIONAL are options, and can be `|` * together. The positional options take an optional number of arguments. * * For example, * * std::string filename * program.add_option("filename", filename, "description of filename"); */ template void add_option( std::string name, ///< The name, long,short T &value, ///< The value std::string description, ///< Description string Combiner options ///< The options (REQUIRED, DEFAULT, POSITIONAL) ) { int curr_num = convert.size(); if(options.required) required.emplace(curr_num); random_name_store.emplace_back(name); random_name_store.emplace_back(description); 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()}); add_option_internal(value); if(options.positional!=0) std::cout << "positional args not yet supported" << std::endl; } /// Adds a flag style option void add_flag(std::string name, std::string description, int& flag) { counts.emplace_back(0); random_name_store.emplace_back(name); random_name_store.emplace_back(description); 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()}); convert.push_back([&flag](std::vector v){flag = v.size(); return true;}); } void add_option_internal(int &val) { convert.push_back([&val](std::vector v){val = std::stoi(v.at(0)); return v.size()==1;}); } void add_option_internal(std::string &val) { convert.push_back([&val](std::vector v){val = v.at(0); return v.size()==1;}); } /// This must be called after the options are in but before the rest of the program. /** Calls the Boost boost::program_options initialization, causing the program to exit * if -h or an invalid option is passed. */ void start() { parse(); } /// Counts the number of times the given option was passed. int count(std::string name) const { return 0; } };