diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ec27df..189aa5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## Version 0.4 (in progress) * Updates to help print +* Removed `run`, please use `parse` unless you subclass and add it ## Version 0.3 diff --git a/README.md b/README.md index 88ae9e3..8d323e1 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ std::string filename = "default"; app.add_option("-f,--file", file, "A help string"); try { - app.run(argc, argv); + app.parse(argc, argv); } catch (const CLI::ParseError &e) { return app.exit(e); } diff --git a/examples/try.cpp b/examples/try.cpp index e298b0f..3d96781 100644 --- a/examples/try.cpp +++ b/examples/try.cpp @@ -15,7 +15,7 @@ int main (int argc, char** argv) { app.add_option("-d,--double", value, "Some Value"); try { - app.run(argc, argv); + app.parse(argc, argv); } catch (const CLI::Error &e) { return app.exit(e); } diff --git a/examples/try1.cpp b/examples/try1.cpp index e42679d..9d1a4cd 100644 --- a/examples/try1.cpp +++ b/examples/try1.cpp @@ -14,7 +14,7 @@ int main (int argc, char** argv) { CLI::Option* s = stop->add_flag("-c,--count", "Counter"); try { - app.run(argc, argv); + app.parse(argc, argv); } catch (const CLI::Error &e) { return app.exit(e); } diff --git a/examples/try2.cpp b/examples/try2.cpp index dca59ce..e085215 100644 --- a/examples/try2.cpp +++ b/examples/try2.cpp @@ -18,7 +18,7 @@ int main (int argc, char** argv) { ->group("Other"); try { - app.run(argc, argv); + app.parse(argc, argv); } catch (const CLI::Error &e) { return app.exit(e); } diff --git a/include/CLI/App.hpp b/include/CLI/App.hpp index e0caa8c..c8cb6d0 100644 --- a/include/CLI/App.hpp +++ b/include/CLI/App.hpp @@ -58,6 +58,15 @@ protected: public: + /// Create a new program. Pass in the same arguments as main(), along with a help string. + App(std::string prog_description="", bool help=true) + : prog_description(prog_description) { + + if(help) + help_flag = add_flag("-h,--help", "Print this help message and exit"); + + } + /// Set a callback for the end of parsing. Due to a bug in c++11, /// it is not possible to overload on std::function (fixed in c++14 @@ -67,11 +76,6 @@ public: app_callback = callback; } - void run_callback() { - if(app_callback) - app_callback(); - } - /// Reset the parsed data void reset() { @@ -97,15 +101,6 @@ public: } - /// Create a new program. Pass in the same arguments as main(), along with a help string. - App(std::string prog_description="", bool help=true) - : prog_description(prog_description) { - - if(help) - help_flag = add_flag("-h,--help", "Print this help message and exit"); - - } - /// Add a subcommand. Like the constructor, you can override the help message addition by setting help=false App* add_subcommand(std::string name, std::string description="", bool help=true) { subcommands.emplace_back(new App(description, help)); @@ -331,6 +326,7 @@ public: virtual void pre_callback() {} /// Parses the command line - throws errors + /// This must be called after the options are in but before the rest of the program. void parse(int argc, char **argv) { progname = argv[0]; std::vector args; @@ -340,7 +336,149 @@ public: } /// The real work is done here. Expects a reversed vector - void parse(std::vector & args, bool first_parse=true) { + void parse(std::vector &args) { + return _parse(args, true); + } + + + /// Print a nice error message and return the exit code + int exit(const Error& e) const { + if(e.exit_code != 0) { + std::cerr << "ERROR: "; + std::cerr << e.what() << std::endl; + if(e.print_help) + std::cerr << help(); + } else { + if(e.print_help) + std::cout << help(); + } + return e.exit_code; + } + + /// Counts the number of times the given option was passed. + int count(std::string name) const { + for(const Option_p &opt : options) { + if(opt->check_name(name)) { + return opt->count(); + } + } + throw OptionNotFound(name); + } + + /// Makes a help message, with a column `wid` for column 1 + std::string help(size_t wid=30, std::string prev="") const { + // Delegate to subcommand if needed + if(prev == "") + prev = progname; + else + prev += " " + name; + + if(subcommand != nullptr) + return subcommand->help(wid, prev); + + std::stringstream out; + out << prog_description << std::endl; + out << "Usage: " << prev; + + // Check for options + bool npos = false; + std::set groups; + for(const Option_p &opt : options) { + if(opt->nonpositional()) { + npos = true; + groups.insert(opt->get_group()); + } + } + + if(npos) + out << " [OPTIONS]"; + + // Positionals + bool pos=false; + for(const Option_p &opt : options) + if(opt->get_positional()) { + out << " " << opt->help_positional(); + if(opt->has_description()) + pos=true; + } + + if(subcommands.size() > 0) + out << " [SUBCOMMANDS]"; + + out << std::endl << std::endl; + + // Positional descriptions + if(pos) { + out << "Positionals:" << std::endl; + for(const Option_p &opt : options) + if(opt->get_positional() && opt->has_description()) + detail::format_help(out, opt->help_pname(), opt->get_description(), wid); + out << std::endl; + + } + + + // Options + if(npos) { + for (const std::string& group : groups) { + out << group << ":" << std::endl; + for(const Option_p &opt : options) { + if(opt->nonpositional() && opt->get_group() == group) + detail::format_help(out, opt->help_name(), opt->get_description(), wid); + + } + out << std::endl; + } + } + + // Subcommands + if(subcommands.size()> 0) { + out << "Subcommands:" << std::endl; + for(const App_p &com : subcommands) + detail::format_help(out, com->get_name(), com->prog_description, wid); + } + return out.str(); + } + + /// Get a subcommand pointer to the currently selected subcommand (after parsing) + App* get_subcommand() { + return subcommand; + } + + /// Get the name of the current app + std::string get_name() const { + return name; + } + + +protected: + + /// Internal function to run (App) callback + void run_callback() { + if(app_callback) + app_callback(); + } + + /// Selects a Classifer enum based on the type of the current argument + Classifer _recognize(std::string current) const { + std::string dummy1, dummy2; + + if(current == "--") + return Classifer::POSITIONAL_MARK; + for(const App_p &com : subcommands) { + if(com->name == current) + return Classifer::SUBCOMMAND; + } + if(detail::split_long(current, dummy1, dummy2)) + return Classifer::LONG; + if(detail::split_short(current, dummy1, dummy2)) + return Classifer::SHORT; + return Classifer::NONE; + } + + + /// Internal parse function + void _parse(std::vector &args, bool first_parse) { parsed = true; bool positional_only = false; @@ -403,7 +541,7 @@ public: std::reverse(std::begin(values), std::end(values)); values.insert(std::begin(values), std::begin(positionals), std::end(positionals)); - return parse(values, false); + return _parse(values, false); } catch (const FileError &e) { if(ini_required) throw; @@ -433,6 +571,7 @@ public: run_callback(); } + void _parse_subcommand(std::vector &args) { for(const App_p &com : subcommands) { if(com->name == args.back()){ @@ -445,6 +584,7 @@ public: throw HorribleError("Subcommand"); } + /// Parse a short argument, must be at the top of the list void _parse_short(std::vector &args) { std::string current = args.back(); @@ -495,22 +635,7 @@ public: } } - Classifer _recognize(std::string current) const { - std::string dummy1, dummy2; - - if(current == "--") - return Classifer::POSITIONAL_MARK; - for(const App_p &com : subcommands) { - if(com->name == current) - return Classifer::SUBCOMMAND; - } - if(detail::split_long(current, dummy1, dummy2)) - return Classifer::LONG; - if(detail::split_short(current, dummy1, dummy2)) - return Classifer::SHORT; - return Classifer::NONE; - } - + /// Parse a long argument, must be at the top of the list void _parse_long(std::vector &args) { std::string current = args.back(); @@ -554,117 +679,6 @@ public: return; } - /// This must be called after the options are in but before the rest of the program. - /** Instead of throwing erros, this gives an error code - * if -h or an invalid option is passed. Continue with your program if returns -1 */ - void run(int argc, char** argv) { - parse(argc, argv); - } - - int exit(const Error& e) const { - if(e.exit_code != 0) { - std::cerr << "ERROR: "; - std::cerr << e.what() << std::endl; - if(e.print_help) - std::cerr << help(); - } else { - if(e.print_help) - std::cout << help(); - } - return e.exit_code; - } - - /// Counts the number of times the given option was passed. - int count(std::string name) const { - for(const Option_p &opt : options) { - if(opt->check_name(name)) { - return opt->count(); - } - } - throw OptionNotFound(name); - } - - std::string help(size_t wid=30, std::string prev="") const { - // Delegate to subcommand if needed - if(prev == "") - prev = progname; - else - prev += " " + name; - - if(subcommand != nullptr) - return subcommand->help(wid, prev); - - std::stringstream out; - out << prog_description << std::endl; - out << "Usage: " << prev; - - // Check for options - bool npos = false; - std::set groups; - for(const Option_p &opt : options) { - if(opt->nonpositional()) { - npos = true; - groups.insert(opt->get_group()); - } - } - - if(npos) - out << " [OPTIONS]"; - - // Positionals - bool pos=false; - for(const Option_p &opt : options) - if(opt->get_positional()) { - out << " " << opt->help_positional(); - if(opt->has_description()) - pos=true; - } - - if(subcommands.size() > 0) - out << " [SUBCOMMANDS]"; - - out << std::endl << std::endl; - - // Positional descriptions - if(pos) { - out << "Positionals:" << std::endl; - for(const Option_p &opt : options) - if(opt->get_positional() && opt->has_description()) - detail::format_help(out, opt->help_pname(), opt->get_description(), wid); - out << std::endl; - - } - - - // Options - if(npos) { - for (const std::string& group : groups) { - out << group << ":" << std::endl; - for(const Option_p &opt : options) { - if(opt->nonpositional() && opt->get_group() == group) - detail::format_help(out, opt->help_name(), opt->get_description(), wid); - - } - out << std::endl; - } - } - - // Subcommands - if(subcommands.size()> 0) { - out << "Subcommands:" << std::endl; - for(const App_p &com : subcommands) - detail::format_help(out, com->get_name(), com->prog_description, wid); - } - return out.str(); - } - - App* get_subcommand() { - return subcommand; - } - - std::string get_name() const { - return name; - } };