mirror of
https://github.com/CLIUtils/CLI11.git
synced 2025-04-30 20:53:52 +00:00
Initial commit of CLI library. A few things working, but not much
This commit is contained in:
commit
2db15139cb
465
CLI.hpp
Normal file
465
CLI.hpp
Normal file
@ -0,0 +1,465 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <regex>
|
||||
#include <iostream>
|
||||
#include <functional>
|
||||
#include <tuple>
|
||||
#include <exception>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <type_traits>
|
||||
|
||||
// This is unreachable outside this file; you should not use Combiner directly
|
||||
namespace {
|
||||
|
||||
void logit(std::string output) {
|
||||
std::cout << output << std::endl;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::string join(const T& v, std::string delim = ",") {
|
||||
std::ostringstream s;
|
||||
for (const auto& i : v) {
|
||||
if (&i != &v[0]) {
|
||||
s << delim;
|
||||
}
|
||||
s << i;
|
||||
}
|
||||
return s.str();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
struct Combiner {
|
||||
int num;
|
||||
bool positional;
|
||||
bool required;
|
||||
bool defaulted;
|
||||
|
||||
/// Can be or-ed together
|
||||
Combiner operator | (Combiner b) const {
|
||||
Combiner self;
|
||||
self.num = num + b.num;
|
||||
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, positional, required, defaulted};
|
||||
}
|
||||
Combiner operator, (Combiner b) const {
|
||||
return *this | b;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
namespace CLI {
|
||||
|
||||
class BadNameString : public std::runtime_error {
|
||||
public:
|
||||
BadNameString(std::string name) : runtime_error("Failed to parse: " + name) {};
|
||||
};
|
||||
|
||||
class CallForHelp : public std::runtime_error {
|
||||
public:
|
||||
CallForHelp() : runtime_error("Help option passed") {};
|
||||
};
|
||||
|
||||
class ParseError : public std::runtime_error {
|
||||
public:
|
||||
ParseError(std::string info="") : runtime_error(info) {};
|
||||
};
|
||||
|
||||
class OptionAlreadyAdded : public std::runtime_error {
|
||||
public:
|
||||
OptionAlreadyAdded(std::string name) : runtime_error("Already added:" + name) {};
|
||||
};
|
||||
|
||||
|
||||
|
||||
const std::regex reg_split{R"regex((?:([a-zA-Z0-9]?)(?:,|$)|^)([a-zA-Z0-9][a-zA-Z0-9_\-]*)?)regex"};
|
||||
const std::regex reg_short{R"regex(-([^-])(.*))regex"};
|
||||
const std::regex reg_long{R"regex(--([^-^=][^=]*)=?(.*))regex"};
|
||||
|
||||
|
||||
std::tuple<std::string, std::string> split(std::string fullname) throw(BadNameString) {
|
||||
|
||||
std::smatch match;
|
||||
if (std::regex_match(fullname, match, reg_split)) {
|
||||
std::string sname = match[1];
|
||||
std::string lname = match[2];
|
||||
if(sname == "" and lname == "")
|
||||
throw BadNameString("EMPTY");
|
||||
return std::tuple<std::string, std::string>(sname, lname);
|
||||
} else throw BadNameString(fullname);
|
||||
}
|
||||
|
||||
const Combiner NOTHING {0,false,false,false};
|
||||
const Combiner REQUIRED {0,false,true, false};
|
||||
const Combiner DEFAULT {0,false,false,true};
|
||||
const Combiner POSITIONAL{0,true, false,false};
|
||||
const Combiner ARGS {1,false,false,false};
|
||||
|
||||
typedef std::vector<std::vector<std::string>> results_t;
|
||||
typedef std::function<bool(results_t)> callback_t;
|
||||
|
||||
class Option {
|
||||
public:
|
||||
protected:
|
||||
// Config
|
||||
std::string sname;
|
||||
std::string lname;
|
||||
Combiner opts;
|
||||
std::string discription;
|
||||
callback_t callback;
|
||||
|
||||
// Results
|
||||
results_t results {};
|
||||
|
||||
|
||||
public:
|
||||
Option(std::string name, std::string discription = "", Combiner opts=NOTHING, std::function<bool(results_t)> callback=[](results_t){return true;}) throw (BadNameString) :
|
||||
opts(opts), discription(discription), callback(callback){
|
||||
std::tie(sname, lname) = split(name);
|
||||
}
|
||||
|
||||
/// Process the callback
|
||||
bool run_callback() const {
|
||||
return callback(results);
|
||||
}
|
||||
|
||||
/// Indistinguishible options are equal
|
||||
bool operator== (const Option& other) const {
|
||||
if(sname=="" && other.sname=="")
|
||||
return lname==other.lname;
|
||||
else if(lname=="" && other.lname=="")
|
||||
return sname==other.sname;
|
||||
else
|
||||
return sname==other.sname || lname==other.lname;
|
||||
}
|
||||
|
||||
std::string getName() const {
|
||||
if(sname=="")
|
||||
return "--" + lname;
|
||||
else if (lname=="")
|
||||
return "-" + sname;
|
||||
else
|
||||
return "-" + sname + ", --" + lname;
|
||||
}
|
||||
|
||||
bool check_sname(const std::string& name) const {
|
||||
return name == sname;
|
||||
}
|
||||
|
||||
bool check_lname(const std::string& name) const {
|
||||
return name == lname;
|
||||
}
|
||||
|
||||
std::string get_sname() const {
|
||||
return sname;
|
||||
}
|
||||
|
||||
std::string get_lname() const {
|
||||
return lname;
|
||||
}
|
||||
|
||||
|
||||
int get_num() const {
|
||||
return opts.num;
|
||||
}
|
||||
|
||||
void add_result(int r, std::string s) {
|
||||
results.at(r).push_back(s);
|
||||
}
|
||||
int get_new() {
|
||||
results.emplace_back();
|
||||
return results.size() - 1;
|
||||
}
|
||||
int count() {
|
||||
return results.size();
|
||||
}
|
||||
|
||||
std::string string() const {
|
||||
std::string val = "Option: " + getName() + "\n"
|
||||
+ " " + discription + "\n"
|
||||
+ " [";
|
||||
for(const auto& item : results) {
|
||||
if(&item!=&results[0])
|
||||
val+="],[";
|
||||
val += join(item);
|
||||
}
|
||||
val += "]";
|
||||
return val;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/// Creates a command line program, with very few defaults.
|
||||
/** To use, create a new Program() instance with argc, argv, and a help discription. 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 App {
|
||||
public:
|
||||
|
||||
protected:
|
||||
|
||||
std::string desc;
|
||||
std::vector<Option> options;
|
||||
std::vector<std::string> missing_options;
|
||||
std::vector<std::string> positionals;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
/// Create a new program. Pass in the same arguments as main(), along with a help string.
|
||||
App(std::string discription)
|
||||
: desc(discription) {
|
||||
}
|
||||
|
||||
/// 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, "discription of filename");
|
||||
*/
|
||||
void add_option(
|
||||
std::string name, ///< The name, long,short
|
||||
callback_t callback, ///< The callback
|
||||
std::string discription="", ///< Discription string
|
||||
Combiner opts=ARGS ///< The options (REQUIRED, DEFAULT, POSITIONAL, ARGS())
|
||||
) {
|
||||
|
||||
Option myopt{name, discription, opts, callback};
|
||||
if(std::find(std::begin(options), std::end(options), myopt) == std::end(options))
|
||||
options.push_back(myopt);
|
||||
else
|
||||
throw OptionAlreadyAdded(myopt.getName());
|
||||
|
||||
}
|
||||
|
||||
/// Add option for string
|
||||
void add_option(
|
||||
std::string name, ///< The name, long,short
|
||||
std::string &variable, ///< The variable to set
|
||||
std::string discription="", ///< Discription string
|
||||
Combiner opts=ARGS ///< The options (REQUIRED, DEFAULT, POSITIONAL, ARGS())
|
||||
) {
|
||||
|
||||
CLI::callback_t fun = [&variable](CLI::results_t res){
|
||||
if(res.size()!=1) {
|
||||
return false;
|
||||
}
|
||||
if(res[0].size()!=1) {
|
||||
return false;
|
||||
}
|
||||
variable = res[0][0];
|
||||
return true;
|
||||
};
|
||||
|
||||
add_option(name, fun, discription, opts);
|
||||
}
|
||||
|
||||
|
||||
/// Add option for flag
|
||||
void add_flag(
|
||||
std::string name, ///< The name, short,long
|
||||
std::string discription="" ///< Discription string
|
||||
) {
|
||||
|
||||
CLI::callback_t fun = [](CLI::results_t res){
|
||||
return true;
|
||||
};
|
||||
|
||||
add_option(name, fun, discription, NOTHING);
|
||||
}
|
||||
|
||||
/// Add option for flag
|
||||
template<typename T>
|
||||
typename std::enable_if<std::is_integral<T>::value, void>::type
|
||||
add_flag(
|
||||
std::string name, ///< The name, short,long
|
||||
T &count, ///< A varaible holding the count
|
||||
std::string discription="" ///< Discription string
|
||||
) {
|
||||
|
||||
count = 0;
|
||||
CLI::callback_t fun = [&count](CLI::results_t res){
|
||||
count = res.size();
|
||||
return true;
|
||||
};
|
||||
|
||||
add_option(name, fun, discription, NOTHING);
|
||||
}
|
||||
|
||||
|
||||
/// Parses the command line - throws errors
|
||||
void parse(int argc, char **argv) throw(CallForHelp, ParseError) {
|
||||
std::vector<std::string> args;
|
||||
for(int i=1; i<argc; i++)
|
||||
args.emplace_back(argv[i]);
|
||||
parse(args);
|
||||
}
|
||||
|
||||
void parse(std::vector<std::string> args) throw(CallForHelp, ParseError) {
|
||||
std::reverse(args.begin(), args.end());
|
||||
|
||||
bool positional_only = false;
|
||||
|
||||
while(args.size()>0) {
|
||||
|
||||
if(args.back() == "--") {
|
||||
args.pop_back();
|
||||
positional_only = true;
|
||||
} else if(positional_only || (!_parse_long(args) && !_parse_short(args))) {
|
||||
|
||||
logit("Positional: "+args.back());
|
||||
positionals.push_back(args.back());
|
||||
args.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
for(Option& opt : options)
|
||||
if (opt.count() > 0) {
|
||||
logit(opt.string());
|
||||
if(!opt.run_callback())
|
||||
logit("Failed");
|
||||
}
|
||||
|
||||
//TODO: Check for false callbacks
|
||||
}
|
||||
|
||||
bool _parse_short(std::vector<std::string> &args) {
|
||||
std::string current = args.back();
|
||||
std::smatch match;
|
||||
|
||||
if(!std::regex_match(current, match, reg_short))
|
||||
return false;
|
||||
|
||||
args.pop_back();
|
||||
std::string name = match[1];
|
||||
std::string rest = match[2];
|
||||
|
||||
logit("Working on short:");
|
||||
logit(name);
|
||||
logit(rest);
|
||||
|
||||
auto op = std::find_if(std::begin(options), std::end(options), [name](const Option &v){return v.check_sname(name);});
|
||||
|
||||
if(op == std::end(options)) {
|
||||
missing_options.push_back("-" + op->get_sname());
|
||||
return true;
|
||||
}
|
||||
|
||||
int vnum = op->get_new();
|
||||
int num = op->get_num();
|
||||
|
||||
|
||||
if(rest != "" && num > 0) {
|
||||
num--;
|
||||
op->add_result(vnum, rest);
|
||||
rest = "";
|
||||
}
|
||||
|
||||
while(num>0) {
|
||||
num--;
|
||||
std::string current = args.back();
|
||||
logit("Adding: "+current);
|
||||
args.pop_back();
|
||||
op->add_result(vnum,current);
|
||||
if(args.size()==0)
|
||||
return true;
|
||||
}
|
||||
|
||||
if(rest != "") {
|
||||
rest = "-" + rest;
|
||||
args.push_back(rest);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _parse_long(std::vector<std::string> &args) {
|
||||
std::string current = args.back();
|
||||
std::smatch match;
|
||||
|
||||
if(!std::regex_match(current, match, reg_long))
|
||||
return false;
|
||||
|
||||
args.pop_back();
|
||||
std::string name = match[1];
|
||||
std::string value = match[2];
|
||||
|
||||
|
||||
logit("Working on long:");
|
||||
logit(name);
|
||||
logit(value);
|
||||
|
||||
auto op = std::find_if(std::begin(options), std::end(options), [name](const Option &v){return v.check_lname(name);});
|
||||
|
||||
if(op == std::end(options)) {
|
||||
missing_options.push_back("--" + op->get_lname());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int vnum = op->get_new();
|
||||
int num = op->get_num();
|
||||
|
||||
|
||||
if(value != "") {
|
||||
num--;
|
||||
op->add_result(vnum, value);
|
||||
}
|
||||
|
||||
while(num>0) {
|
||||
num--;
|
||||
std::string current = args.back();
|
||||
args.pop_back();
|
||||
op->add_result(vnum,current);
|
||||
if(args.size()==0)
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// This must be called after the options are in but before the rest of the program.
|
||||
/** Instead of throwing erros, causes the program to exit
|
||||
* if -h or an invalid option is passed. */
|
||||
void start(int argc, char** argv) {
|
||||
try {
|
||||
parse(argc, argv);
|
||||
} catch(const CallForHelp &e) {
|
||||
std::cout << help() << std::endl;
|
||||
exit(0);
|
||||
} catch(const ParseError &e) {
|
||||
std::cerr << "ERROR:" << std::endl;
|
||||
std::cerr << e.what() << std::endl;
|
||||
std::cerr << help() << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Counts the number of times the given option was passed.
|
||||
int count(std::string name) const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string help() const {return "";}
|
||||
|
||||
|
||||
};
|
||||
}
|
140
Program.hpp
Normal file
140
Program.hpp
Normal file
@ -0,0 +1,140 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
|
||||
|
||||
// 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};
|
||||
}
|
||||
Combiner operator, (Combiner b) const {
|
||||
return *this | b;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// 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:
|
||||
boost::program_options::options_description desc;
|
||||
boost::program_options::positional_options_description p;
|
||||
boost::program_options::variables_map vm;
|
||||
|
||||
int argc;
|
||||
char **argv;
|
||||
|
||||
/// Parses the command line (internal function)
|
||||
void parse() {
|
||||
try {
|
||||
boost::program_options::store(boost::program_options::command_line_parser(argc, argv)
|
||||
.options(desc).positional(p).run(), vm);
|
||||
|
||||
if(vm.count("help")){
|
||||
std::cout << desc;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
boost::program_options::notify(vm);
|
||||
} catch(const boost::program_options::error& e) {
|
||||
std::cerr << "ERROR: " << e.what() << std::endl << std::endl;
|
||||
std::cerr << desc << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/// Create a new program. Pass in the same arguments as main(), along with a help string.
|
||||
Program(int argc, char** argv, std::string discription)
|
||||
: argc(argc), argv(argv), desc(discription) {
|
||||
desc.add_options()
|
||||
("help,h", "Display this help message");
|
||||
}
|
||||
|
||||
/// Allows you to manually add options in the boost style.
|
||||
/** Usually the specialized methods are easier, but this remains for people used to Boost and for
|
||||
* unusual situations. */
|
||||
boost::program_options::options_description_easy_init add_options() {
|
||||
return desc.add_options();
|
||||
}
|
||||
|
||||
/// 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<typename T>
|
||||
void add_option(
|
||||
std::string name, ///< The name, long,short
|
||||
T &value, ///< The value
|
||||
std::string description, ///< Discription string
|
||||
Combiner options ///< The options (REQUIRED, DEFAULT, POSITIONAL)
|
||||
) {
|
||||
auto po_value = boost::program_options::value<T>(&value);
|
||||
if(options.defaulted)
|
||||
po_value = po_value->default_value(value);
|
||||
if(options.required)
|
||||
po_value = po_value->required();
|
||||
desc.add_options()(name.c_str(),po_value,description.c_str());
|
||||
if(options.positional!=0)
|
||||
p.add(name.c_str(), options.positional);
|
||||
}
|
||||
|
||||
/// Adds a flag style option
|
||||
void add_option(std::string name, std::string description) {
|
||||
desc.add_options()(name.c_str(),description.c_str());
|
||||
}
|
||||
|
||||
|
||||
/// 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 vm.count(name.c_str());
|
||||
}
|
||||
|
||||
|
||||
};
|
174
ProgramOP.hpp
Normal file
174
ProgramOP.hpp
Normal file
@ -0,0 +1,174 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
#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<option::Descriptor> usage;
|
||||
std::vector<std::function<bool(std::vector<std::string>)>> convert; /// Number is loc+2
|
||||
std::unordered_set<int> required;
|
||||
std::vector<int> counts;
|
||||
std::vector<std::string> 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<option::Option> options(stats.options_max);
|
||||
std::vector<option::Option> 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<convert.size()+2; i++) {
|
||||
counts.emplace_back(options[i].count());
|
||||
if(options[i]) {
|
||||
std::vector<std::string> 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<typename T>
|
||||
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<std::string> v){flag = v.size(); return true;});
|
||||
}
|
||||
|
||||
void add_option_internal(int &val) {
|
||||
convert.push_back([&val](std::vector<std::string> v){val = std::stoi(v.at(0)); return v.size()==1;});
|
||||
}
|
||||
|
||||
void add_option_internal(std::string &val) {
|
||||
convert.push_back([&val](std::vector<std::string> 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;
|
||||
}
|
||||
|
||||
|
||||
};
|
20
try.cpp
Normal file
20
try.cpp
Normal file
@ -0,0 +1,20 @@
|
||||
#include "CLI.hpp"
|
||||
|
||||
|
||||
int main (int argc, char** argv) {
|
||||
|
||||
CLI::App app("K3Pi goofit fitter");
|
||||
|
||||
std::string file;
|
||||
app.add_option("f,file", file, "File name");
|
||||
|
||||
int count;
|
||||
app.add_flag<int>("c,count", count, "File name");
|
||||
|
||||
app.parse(argc, argv);
|
||||
|
||||
std::cout << "Working on file: " << file << std::endl;
|
||||
std::cout << "Working on count: " << count << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
32
try1.cpp
Normal file
32
try1.cpp
Normal file
@ -0,0 +1,32 @@
|
||||
#include "CLI.hpp"
|
||||
|
||||
|
||||
int main (int argc, char** argv) {
|
||||
|
||||
std::vector<std::string> test_strings = {"a,boo", ",coo", "d,", "Q,this-is", "s", "single"};
|
||||
|
||||
for(std::string name : test_strings) {
|
||||
std::string one;
|
||||
std::string two;
|
||||
|
||||
std::tie(one, two) = CLI::split(name);
|
||||
std::cout << one << ", " << two << std::endl;
|
||||
}
|
||||
|
||||
std::vector<std::string> test_fails= {"a,,boo", "a,b,c", "ssd,sfd", "-a", "", ",", "one two"};
|
||||
|
||||
for(std::string name : test_fails) {
|
||||
std::string one;
|
||||
std::string two;
|
||||
|
||||
try {
|
||||
std::tie(one, two) = CLI::split(name);
|
||||
std::cout << "Failed to catch: " << name << std::endl;
|
||||
return 1;
|
||||
} catch (const CLI::BadNameString &e) {
|
||||
std::cout << "Hooray! Caught: " << name << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user