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,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&amp; output) { @@ -297,22 +297,21 @@ bool lexical_cast(std::string input, T&amp; 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 +}