Commit b935fcd12998c01841d72b0a62e1542708c3d347
1 parent
ce308129
Fixes for subcommands, still can cause allocate errors
Showing
2 changed files
with
70 additions
and
41 deletions
CLI.hpp
| @@ -17,7 +17,7 @@ | @@ -17,7 +17,7 @@ | ||
| 17 | namespace { | 17 | namespace { |
| 18 | 18 | ||
| 19 | void logit(std::string output) { | 19 | void logit(std::string output) { |
| 20 | - std::cout << "std::cout << "\033[1;31m" << output << "\033[0m" << std::endl;33[1;31m" << output << "std::cout << "\033[1;31m" << output << "\033[0m" << std::endl;33[0m" << std::endl; | 20 | + //std::cout << "std::cout << "\033[1;31m" << output << "\033[0m" << std::endl;33[1;31m" << output << "std::cout << "\033[1;31m" << output << "\033[0m" << std::endl;33[0m" << std::endl; |
| 21 | } | 21 | } |
| 22 | 22 | ||
| 23 | template <typename T> | 23 | template <typename T> |
| @@ -64,51 +64,51 @@ namespace CLI { | @@ -64,51 +64,51 @@ namespace CLI { | ||
| 64 | 64 | ||
| 65 | class Error : public std::runtime_error { | 65 | class Error : public std::runtime_error { |
| 66 | public: | 66 | public: |
| 67 | - Error(std::string name) : runtime_error(name) {}; | 67 | + Error(std::string parent, std::string name) : runtime_error(parent + ": " + name) {} |
| 68 | }; | 68 | }; |
| 69 | 69 | ||
| 70 | class BadNameString : public Error { | 70 | class BadNameString : public Error { |
| 71 | public: | 71 | public: |
| 72 | - BadNameString(std::string name) : Error("Failed to parse: " + name) {}; | 72 | + BadNameString(std::string name) : Error("BadNameString", name) {} |
| 73 | }; | 73 | }; |
| 74 | 74 | ||
| 75 | class CallForHelp : public Error { | 75 | class CallForHelp : public Error { |
| 76 | public: | 76 | public: |
| 77 | - CallForHelp() : Error("Help option passed") {}; | 77 | + CallForHelp() : Error("CallForHelp","") {} |
| 78 | }; | 78 | }; |
| 79 | 79 | ||
| 80 | class ParseError : public Error { | 80 | class ParseError : public Error { |
| 81 | public: | 81 | public: |
| 82 | - ParseError(std::string info="") : Error(info) {}; | 82 | + ParseError(std::string name) : Error("ParseError", name) {} |
| 83 | }; | 83 | }; |
| 84 | 84 | ||
| 85 | class OptionAlreadyAdded : public Error { | 85 | class OptionAlreadyAdded : public Error { |
| 86 | public: | 86 | public: |
| 87 | - OptionAlreadyAdded(std::string name) : Error("Already added:" + name) {}; | 87 | + OptionAlreadyAdded(std::string name) : Error("OptionAlreadyAdded", name) {} |
| 88 | }; | 88 | }; |
| 89 | 89 | ||
| 90 | class OptionNotFound : public Error { | 90 | class OptionNotFound : public Error { |
| 91 | public: | 91 | public: |
| 92 | - OptionNotFound(std::string name) : Error(name) {}; | 92 | + OptionNotFound(std::string name) : Error("OptionNotFound", name) {} |
| 93 | }; | 93 | }; |
| 94 | 94 | ||
| 95 | class RequiredError : public Error { | 95 | class RequiredError : public Error { |
| 96 | public: | 96 | public: |
| 97 | - RequiredError(std::string name="") : Error(name) {}; | 97 | + RequiredError(std::string name) : Error("RequiredError", name) {} |
| 98 | }; | 98 | }; |
| 99 | 99 | ||
| 100 | class ExtraPositionalsError : public Error { | 100 | class ExtraPositionalsError : public Error { |
| 101 | public: | 101 | public: |
| 102 | - ExtraPositionalsError(std::string name="") : Error(name) {}; | 102 | + ExtraPositionalsError(std::string name) : Error("ExtraPositionalsError", name) {} |
| 103 | }; | 103 | }; |
| 104 | 104 | ||
| 105 | class HorribleError : public Error { | 105 | class HorribleError : public Error { |
| 106 | public: | 106 | public: |
| 107 | - HorribleError(std::string name="") : Error("You should never see this error! "+name) {}; | 107 | + HorribleError(std::string name) : Error("HorribleError", "(You should never see this error) " + name) {} |
| 108 | }; | 108 | }; |
| 109 | class IncorrectConstruction : public Error { | 109 | class IncorrectConstruction : public Error { |
| 110 | public: | 110 | public: |
| 111 | - IncorrectConstruction(std::string name="") : Error(name) {}; | 111 | + IncorrectConstruction(std::string name) : Error("IncorrectConstruction", name) {} |
| 112 | }; | 112 | }; |
| 113 | 113 | ||
| 114 | const std::regex reg_split{R"regex((?:([a-zA-Z0-9]?)(?:,|$)|^)([a-zA-Z0-9][a-zA-Z0-9_\-]*)?)regex"}; | 114 | const std::regex reg_split{R"regex((?:([a-zA-Z0-9]?)(?:,|$)|^)([a-zA-Z0-9][a-zA-Z0-9_\-]*)?)regex"}; |
| @@ -297,22 +297,21 @@ bool lexical_cast(std::string input, T& output) { | @@ -297,22 +297,21 @@ bool lexical_cast(std::string input, T& output) { | ||
| 297 | 297 | ||
| 298 | enum class Classifer {NONE, POSITIONAL_MARK, SHORT, LONG, SUBCOMMAND}; | 298 | enum class Classifer {NONE, POSITIONAL_MARK, SHORT, LONG, SUBCOMMAND}; |
| 299 | 299 | ||
| 300 | +class App; | ||
| 300 | 301 | ||
| 301 | /// Creates a command line program, with very few defaults. | 302 | /// Creates a command line program, with very few defaults. |
| 302 | /** To use, create a new Program() instance with argc, argv, and a help discription. The templated | 303 | /** To use, create a new Program() instance with argc, argv, and a help discription. The templated |
| 303 | * add_option methods make it easy to prepare options. Remember to call `.start` before starting your | 304 | * add_option methods make it easy to prepare options. Remember to call `.start` before starting your |
| 304 | * program, so that the options can be evaluated and the help option doesn't accidentally run your program. */ | 305 | * program, so that the options can be evaluated and the help option doesn't accidentally run your program. */ |
| 305 | class App { | 306 | class App { |
| 306 | -public: | ||
| 307 | - | ||
| 308 | protected: | 307 | protected: |
| 309 | 308 | ||
| 310 | std::string name; | 309 | std::string name; |
| 311 | - std::string discription; | 310 | + std::string prog_discription; |
| 312 | std::vector<Option> options; | 311 | std::vector<Option> options; |
| 313 | std::vector<std::string> missing_options; | 312 | std::vector<std::string> missing_options; |
| 314 | std::vector<std::string> positionals; | 313 | std::vector<std::string> positionals; |
| 315 | - std::vector<App> subcommands; | 314 | + std::vector<App*> subcommands; |
| 316 | bool parsed{false}; | 315 | bool parsed{false}; |
| 317 | App* subcommand = nullptr; | 316 | App* subcommand = nullptr; |
| 318 | 317 | ||
| @@ -320,16 +319,22 @@ public: | @@ -320,16 +319,22 @@ public: | ||
| 320 | 319 | ||
| 321 | 320 | ||
| 322 | /// Create a new program. Pass in the same arguments as main(), along with a help string. | 321 | /// Create a new program. Pass in the same arguments as main(), along with a help string. |
| 323 | - App(std::string discription="") | ||
| 324 | - : discription(discription) { | 322 | + App(std::string prog_discription="") |
| 323 | + : prog_discription(prog_discription) { | ||
| 325 | 324 | ||
| 326 | add_flag("h,help", "Print this help message and exit"); | 325 | add_flag("h,help", "Print this help message and exit"); |
| 327 | 326 | ||
| 328 | } | 327 | } |
| 329 | 328 | ||
| 330 | - App& add_subcommand(std::string name) { | ||
| 331 | - subcommands.emplace_back(); | ||
| 332 | - subcommands.back().name = name; | 329 | + ~App() { |
| 330 | + for(App* app : subcommands) | ||
| 331 | + delete app; | ||
| 332 | + } | ||
| 333 | + | ||
| 334 | + App* add_subcommand(std::string name, std::string discription="") { | ||
| 335 | + subcommands.push_back(new App(discription)); | ||
| 336 | + subcommands.back()->name = name; | ||
| 337 | + logit(subcommands.back()->name); | ||
| 333 | return subcommands.back(); | 338 | return subcommands.back(); |
| 334 | } | 339 | } |
| 335 | /// Add an option, will automatically understand the type for common types. | 340 | /// Add an option, will automatically understand the type for common types. |
| @@ -514,7 +519,6 @@ public: | @@ -514,7 +519,6 @@ public: | ||
| 514 | throw CallForHelp(); | 519 | throw CallForHelp(); |
| 515 | } | 520 | } |
| 516 | 521 | ||
| 517 | - bool success = true; | ||
| 518 | for(Option& opt : options) { | 522 | for(Option& opt : options) { |
| 519 | while (opt.positional() && opt.count() < opt.expected() && positionals.size() > 0) { | 523 | while (opt.positional() && opt.count() < opt.expected() && positionals.size() > 0) { |
| 520 | opt.get_new(); | 524 | opt.get_new(); |
| @@ -524,22 +528,21 @@ public: | @@ -524,22 +528,21 @@ public: | ||
| 524 | if (opt.required() && opt.count() < opt.expected()) | 528 | if (opt.required() && opt.count() < opt.expected()) |
| 525 | throw RequiredError(opt.get_name()); | 529 | throw RequiredError(opt.get_name()); |
| 526 | if (opt.count() > 0) { | 530 | if (opt.count() > 0) { |
| 527 | - success &= opt.run_callback(); | 531 | + if(!opt.run_callback()) |
| 532 | + throw ParseError(opt.get_name()); | ||
| 528 | } | 533 | } |
| 529 | 534 | ||
| 530 | } | 535 | } |
| 531 | - if(!success) | ||
| 532 | - throw ParseError(); | ||
| 533 | if(positionals.size()>0) | 536 | if(positionals.size()>0) |
| 534 | - throw ExtraPositionalsError(); | 537 | + throw ExtraPositionalsError("[" + join(positionals) + "]"); |
| 535 | } | 538 | } |
| 536 | 539 | ||
| 537 | void _parse_subcommand(std::vector<std::string> &args) { | 540 | void _parse_subcommand(std::vector<std::string> &args) { |
| 538 | - for(App &com : subcommands) { | ||
| 539 | - if(com.name == args.back()){ | 541 | + for(App *com : subcommands) { |
| 542 | + if(com->name == args.back()){ | ||
| 540 | args.pop_back(); | 543 | args.pop_back(); |
| 541 | - com.parse(args); | ||
| 542 | - subcommand = &com; | 544 | + com->parse(args); |
| 545 | + subcommand = com; | ||
| 543 | return; | 546 | return; |
| 544 | } | 547 | } |
| 545 | } | 548 | } |
| @@ -551,7 +554,7 @@ public: | @@ -551,7 +554,7 @@ public: | ||
| 551 | std::smatch match; | 554 | std::smatch match; |
| 552 | 555 | ||
| 553 | if(!std::regex_match(current, match, reg_short)) | 556 | if(!std::regex_match(current, match, reg_short)) |
| 554 | - throw HorribleError("Subcommand"); | 557 | + throw HorribleError("Short"); |
| 555 | 558 | ||
| 556 | args.pop_back(); | 559 | args.pop_back(); |
| 557 | std::string name = match[1]; | 560 | std::string name = match[1]; |
| @@ -608,8 +611,8 @@ public: | @@ -608,8 +611,8 @@ public: | ||
| 608 | Classifer _recognize(std::string current) const { | 611 | Classifer _recognize(std::string current) const { |
| 609 | if(current == "--") | 612 | if(current == "--") |
| 610 | return Classifer::POSITIONAL_MARK; | 613 | return Classifer::POSITIONAL_MARK; |
| 611 | - for(const App &com : subcommands) { | ||
| 612 | - if(com.name == current) | 614 | + for(const App* com : subcommands) { |
| 615 | + if(com->name == current) | ||
| 613 | return Classifer::SUBCOMMAND; | 616 | return Classifer::SUBCOMMAND; |
| 614 | } | 617 | } |
| 615 | if(std::regex_match(current, reg_long)) | 618 | if(std::regex_match(current, reg_long)) |
| @@ -685,7 +688,7 @@ public: | @@ -685,7 +688,7 @@ public: | ||
| 685 | std::cout << help() << std::endl; | 688 | std::cout << help() << std::endl; |
| 686 | exit(0); | 689 | exit(0); |
| 687 | } catch(const Error &e) { | 690 | } catch(const Error &e) { |
| 688 | - std::cerr << "ERROR:" << std::endl; | 691 | + std::cerr << "ERROR: "; |
| 689 | std::cerr << e.what() << std::endl; | 692 | std::cerr << e.what() << std::endl; |
| 690 | std::cerr << help() << std::endl; | 693 | std::cerr << help() << std::endl; |
| 691 | exit(1); | 694 | exit(1); |
| @@ -707,21 +710,22 @@ public: | @@ -707,21 +710,22 @@ public: | ||
| 707 | 710 | ||
| 708 | std::string help() const { | 711 | std::string help() const { |
| 709 | std::stringstream out; | 712 | std::stringstream out; |
| 710 | - out << discription << std::endl; | 713 | + if(name != "") |
| 714 | + out << "Subcommand: " << name << " "; | ||
| 715 | + out << prog_discription << std::endl; | ||
| 711 | int len = std::accumulate(std::begin(options), std::end(options), 0, | 716 | int len = std::accumulate(std::begin(options), std::end(options), 0, |
| 712 | [](int val, const Option &opt){ | 717 | [](int val, const Option &opt){ |
| 713 | - return std::max(opt.help_len(), val);}); | 718 | + return std::max(opt.help_len()+1, val);}); |
| 714 | for(const Option &opt : options) { | 719 | for(const Option &opt : options) { |
| 715 | out << opt.help(len) << std::endl; | 720 | out << opt.help(len) << std::endl; |
| 716 | } | 721 | } |
| 717 | if(subcommands.size()> 0) { | 722 | if(subcommands.size()> 0) { |
| 718 | - out << "Subcommands: "; | ||
| 719 | - for(const App &com : subcommands) { | ||
| 720 | - if(&com != &subcommands[0]) | ||
| 721 | - out << ", "; | ||
| 722 | - std::cout << com.get_name(); | 723 | + out << "Subcommands:" << std::endl; |
| 724 | + int max = std::accumulate(std::begin(subcommands), std::end(subcommands), 0, | ||
| 725 | + [](int i, const App* j){return std::max(i, (int) j->get_name().length()+1);}); | ||
| 726 | + for(const App* com : subcommands) { | ||
| 727 | + out << std::setw(max) << std::left << com->get_name() << " " << com->prog_discription << std::endl; | ||
| 723 | } | 728 | } |
| 724 | - out << std::endl; | ||
| 725 | } | 729 | } |
| 726 | return out.str(); | 730 | return out.str(); |
| 727 | } | 731 | } |
try2.cpp
0 → 100644
| 1 | +#include "CLI.hpp" | ||
| 2 | + | ||
| 3 | + | ||
| 4 | +int main (int argc, char** argv) { | ||
| 5 | + | ||
| 6 | + CLI::App app("K3Pi goofit fitter"); | ||
| 7 | + CLI::App* start = app.add_subcommand("start"); | ||
| 8 | + CLI::App* stop = app.add_subcommand("stop"); | ||
| 9 | + | ||
| 10 | + std::cout << app.help(); | ||
| 11 | + std::string file; | ||
| 12 | + start->add_option("f,file", file, "File name"); | ||
| 13 | + | ||
| 14 | + int count; | ||
| 15 | + stop->add_flag("c,count", count, "File name"); | ||
| 16 | + | ||
| 17 | + app.start(argc, argv); | ||
| 18 | + | ||
| 19 | + std::cout << "Working on file: " << file << ", direct count: " << start->count("file") << std::endl; | ||
| 20 | + std::cout << "Working on count: " << count << ", direct count: " << stop->count("count") << std::endl; | ||
| 21 | + if(app.get_subcommand() != nullptr) | ||
| 22 | + std::cout << "Subcommand:" << app.get_subcommand()->get_name() << std::endl; | ||
| 23 | + | ||
| 24 | + return 0; | ||
| 25 | +} |