Commit b935fcd12998c01841d72b0a62e1542708c3d347

Authored by Henry Fredrick Schreiner
1 parent ce308129

Fixes for subcommands, still can cause allocate errors

Showing 2 changed files with 70 additions and 41 deletions
... ... @@ -17,7 +17,7 @@
17 17 namespace {
18 18  
19 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 23 template <typename T>
... ... @@ -64,51 +64,51 @@ namespace CLI {
64 64  
65 65 class Error : public std::runtime_error {
66 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 70 class BadNameString : public Error {
71 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 75 class CallForHelp : public Error {
76 76 public:
77   - CallForHelp() : Error("Help option passed") {};
  77 + CallForHelp() : Error("CallForHelp","") {}
78 78 };
79 79  
80 80 class ParseError : public Error {
81 81 public:
82   - ParseError(std::string info="") : Error(info) {};
  82 + ParseError(std::string name) : Error("ParseError", name) {}
83 83 };
84 84  
85 85 class OptionAlreadyAdded : public Error {
86 86 public:
87   - OptionAlreadyAdded(std::string name) : Error("Already added:" + name) {};
  87 + OptionAlreadyAdded(std::string name) : Error("OptionAlreadyAdded", name) {}
88 88 };
89 89  
90 90 class OptionNotFound : public Error {
91 91 public:
92   - OptionNotFound(std::string name) : Error(name) {};
  92 + OptionNotFound(std::string name) : Error("OptionNotFound", name) {}
93 93 };
94 94  
95 95 class RequiredError : public Error {
96 96 public:
97   - RequiredError(std::string name="") : Error(name) {};
  97 + RequiredError(std::string name) : Error("RequiredError", name) {}
98 98 };
99 99  
100 100 class ExtraPositionalsError : public Error {
101 101 public:
102   - ExtraPositionalsError(std::string name="") : Error(name) {};
  102 + ExtraPositionalsError(std::string name) : Error("ExtraPositionalsError", name) {}
103 103 };
104 104  
105 105 class HorribleError : public Error {
106 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 109 class IncorrectConstruction : public Error {
110 110 public:
111   - IncorrectConstruction(std::string name="") : Error(name) {};
  111 + IncorrectConstruction(std::string name) : Error("IncorrectConstruction", name) {}
112 112 };
113 113  
114 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&amp; output) {
297 297  
298 298 enum class Classifer {NONE, POSITIONAL_MARK, SHORT, LONG, SUBCOMMAND};
299 299  
  300 +class App;
300 301  
301 302 /// Creates a command line program, with very few defaults.
302 303 /** To use, create a new Program() instance with argc, argv, and a help discription. The templated
303 304 * add_option methods make it easy to prepare options. Remember to call `.start` before starting your
304 305 * program, so that the options can be evaluated and the help option doesn't accidentally run your program. */
305 306 class App {
306   -public:
307   -
308 307 protected:
309 308  
310 309 std::string name;
311   - std::string discription;
  310 + std::string prog_discription;
312 311 std::vector<Option> options;
313 312 std::vector<std::string> missing_options;
314 313 std::vector<std::string> positionals;
315   - std::vector<App> subcommands;
  314 + std::vector<App*> subcommands;
316 315 bool parsed{false};
317 316 App* subcommand = nullptr;
318 317  
... ... @@ -320,16 +319,22 @@ public:
320 319  
321 320  
322 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 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 338 return subcommands.back();
334 339 }
335 340 /// Add an option, will automatically understand the type for common types.
... ... @@ -514,7 +519,6 @@ public:
514 519 throw CallForHelp();
515 520 }
516 521  
517   - bool success = true;
518 522 for(Option& opt : options) {
519 523 while (opt.positional() && opt.count() < opt.expected() && positionals.size() > 0) {
520 524 opt.get_new();
... ... @@ -524,22 +528,21 @@ public:
524 528 if (opt.required() && opt.count() < opt.expected())
525 529 throw RequiredError(opt.get_name());
526 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 536 if(positionals.size()>0)
534   - throw ExtraPositionalsError();
  537 + throw ExtraPositionalsError("[" + join(positionals) + "]");
535 538 }
536 539  
537 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 543 args.pop_back();
541   - com.parse(args);
542   - subcommand = &com;
  544 + com->parse(args);
  545 + subcommand = com;
543 546 return;
544 547 }
545 548 }
... ... @@ -551,7 +554,7 @@ public:
551 554 std::smatch match;
552 555  
553 556 if(!std::regex_match(current, match, reg_short))
554   - throw HorribleError("Subcommand");
  557 + throw HorribleError("Short");
555 558  
556 559 args.pop_back();
557 560 std::string name = match[1];
... ... @@ -608,8 +611,8 @@ public:
608 611 Classifer _recognize(std::string current) const {
609 612 if(current == "--")
610 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 616 return Classifer::SUBCOMMAND;
614 617 }
615 618 if(std::regex_match(current, reg_long))
... ... @@ -685,7 +688,7 @@ public:
685 688 std::cout << help() << std::endl;
686 689 exit(0);
687 690 } catch(const Error &e) {
688   - std::cerr << "ERROR:" << std::endl;
  691 + std::cerr << "ERROR: ";
689 692 std::cerr << e.what() << std::endl;
690 693 std::cerr << help() << std::endl;
691 694 exit(1);
... ... @@ -707,21 +710,22 @@ public:
707 710  
708 711 std::string help() const {
709 712 std::stringstream out;
710   - out << discription << std::endl;
  713 + if(name != "")
  714 + out << "Subcommand: " << name << " ";
  715 + out << prog_discription << std::endl;
711 716 int len = std::accumulate(std::begin(options), std::end(options), 0,
712 717 [](int val, const Option &opt){
713   - return std::max(opt.help_len(), val);});
  718 + return std::max(opt.help_len()+1, val);});
714 719 for(const Option &opt : options) {
715 720 out << opt.help(len) << std::endl;
716 721 }
717 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 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 +}
... ...