Commit ce3081299825e6aaf5529b13f0b248a86b394722

Authored by Henry Fredrick Schreiner
1 parent 2db15139

Adding better parser

Showing 3 changed files with 358 additions and 85 deletions
.gitignore 0 → 100644
  1 +a.out*
... ...
... ... @@ -9,12 +9,15 @@
9 9 #include <algorithm>
10 10 #include <sstream>
11 11 #include <type_traits>
  12 +#include <unordered_set>
  13 +#include <iomanip>
  14 +#include <numeric>
12 15  
13 16 // This is unreachable outside this file; you should not use Combiner directly
14 17 namespace {
15 18  
16 19 void logit(std::string output) {
17   - std::cout << output << std::endl;
  20 + std::cout << "\033[1;31m" << output << "\033[0m" << std::endl;
18 21 }
19 22  
20 23 template <typename T>
... ... @@ -30,8 +33,6 @@ std::string join(const T&amp; v, std::string delim = &quot;,&quot;) {
30 33 }
31 34  
32 35  
33   -
34   -
35 36 struct Combiner {
36 37 int num;
37 38 bool positional;
... ... @@ -41,7 +42,7 @@ struct Combiner {
41 42 /// Can be or-ed together
42 43 Combiner operator | (Combiner b) const {
43 44 Combiner self;
44   - self.num = num + b.num;
  45 + self.num = std::min(num, b.num) == -1 ? -1 : std::max(num, b.num);
45 46 self.positional = positional || b.positional;
46 47 self.required = required || b.required;
47 48 self.defaulted = defaulted || b.defaulted;
... ... @@ -56,40 +57,66 @@ struct Combiner {
56 57 return *this | b;
57 58 }
58 59 };
59   -
60   -
61 60 }
62 61  
  62 +
63 63 namespace CLI {
64 64  
65   -class BadNameString : public std::runtime_error {
  65 +class Error : public std::runtime_error {
66 66 public:
67   - BadNameString(std::string name) : runtime_error("Failed to parse: " + name) {};
  67 + Error(std::string name) : runtime_error(name) {};
68 68 };
69 69  
70   -class CallForHelp : public std::runtime_error {
  70 +class BadNameString : public Error {
71 71 public:
72   - CallForHelp() : runtime_error("Help option passed") {};
  72 + BadNameString(std::string name) : Error("Failed to parse: " + name) {};
73 73 };
74 74  
75   -class ParseError : public std::runtime_error {
  75 +class CallForHelp : public Error {
76 76 public:
77   - ParseError(std::string info="") : runtime_error(info) {};
  77 + CallForHelp() : Error("Help option passed") {};
78 78 };
79 79  
80   -class OptionAlreadyAdded : public std::runtime_error {
  80 +class ParseError : public Error {
81 81 public:
82   - OptionAlreadyAdded(std::string name) : runtime_error("Already added:" + name) {};
  82 + ParseError(std::string info="") : Error(info) {};
83 83 };
84 84  
  85 +class OptionAlreadyAdded : public Error {
  86 +public:
  87 + OptionAlreadyAdded(std::string name) : Error("Already added:" + name) {};
  88 +};
85 89  
  90 +class OptionNotFound : public Error {
  91 +public:
  92 + OptionNotFound(std::string name) : Error(name) {};
  93 +};
  94 +
  95 +class RequiredError : public Error {
  96 +public:
  97 + RequiredError(std::string name="") : Error(name) {};
  98 +};
  99 +
  100 +class ExtraPositionalsError : public Error {
  101 +public:
  102 + ExtraPositionalsError(std::string name="") : Error(name) {};
  103 +};
  104 +
  105 +class HorribleError : public Error {
  106 +public:
  107 + HorribleError(std::string name="") : Error("You should never see this error! "+name) {};
  108 +};
  109 +class IncorrectConstruction : public Error {
  110 +public:
  111 + IncorrectConstruction(std::string name="") : Error(name) {};
  112 +};
86 113  
87 114 const std::regex reg_split{R"regex((?:([a-zA-Z0-9]?)(?:,|$)|^)([a-zA-Z0-9][a-zA-Z0-9_\-]*)?)regex"};
88 115 const std::regex reg_short{R"regex(-([^-])(.*))regex"};
89 116 const std::regex reg_long{R"regex(--([^-^=][^=]*)=?(.*))regex"};
90 117  
91 118  
92   -std::tuple<std::string, std::string> split(std::string fullname) throw(BadNameString) {
  119 +std::tuple<std::string, std::string> split(std::string fullname) {
93 120  
94 121 std::smatch match;
95 122 if (std::regex_match(fullname, match, reg_split)) {
... ... @@ -101,11 +128,12 @@ std::tuple&lt;std::string, std::string&gt; split(std::string fullname) throw(BadNameSt
101 128 } else throw BadNameString(fullname);
102 129 }
103 130  
104   -const Combiner NOTHING {0,false,false,false};
105   -const Combiner REQUIRED {0,false,true, false};
106   -const Combiner DEFAULT {0,false,false,true};
107   -const Combiner POSITIONAL{0,true, false,false};
108   -const Combiner ARGS {1,false,false,false};
  131 +const Combiner NOTHING {0, false,false,false};
  132 +const Combiner REQUIRED {1, false,true, false};
  133 +const Combiner DEFAULT {1, false,false,true};
  134 +const Combiner POSITIONAL{1, true, false,false};
  135 +const Combiner ARGS {1, false,false,false};
  136 +const Combiner UNLIMITED {-1,false,false,false};
109 137  
110 138 typedef std::vector<std::vector<std::string>> results_t;
111 139 typedef std::function<bool(results_t)> callback_t;
... ... @@ -125,11 +153,27 @@ protected:
125 153  
126 154  
127 155 public:
128   - Option(std::string name, std::string discription = "", Combiner opts=NOTHING, std::function<bool(results_t)> callback=[](results_t){return true;}) throw (BadNameString) :
  156 + Option(std::string name, std::string discription = "", Combiner opts=NOTHING, std::function<bool(results_t)> callback=[](results_t){return true;}) :
129 157 opts(opts), discription(discription), callback(callback){
130 158 std::tie(sname, lname) = split(name);
131 159 }
132 160  
  161 + bool required() const {
  162 + return opts.required;
  163 + }
  164 +
  165 + int expected() const {
  166 + return opts.num;
  167 + }
  168 +
  169 + bool positional() const {
  170 + return opts.positional;
  171 + }
  172 +
  173 + bool defaulted() const {
  174 + return opts.defaulted;
  175 + }
  176 +
133 177 /// Process the callback
134 178 bool run_callback() const {
135 179 return callback(results);
... ... @@ -145,7 +189,7 @@ public:
145 189 return sname==other.sname || lname==other.lname;
146 190 }
147 191  
148   - std::string getName() const {
  192 + std::string get_name() const {
149 193 if(sname=="")
150 194 return "--" + lname;
151 195 else if (lname=="")
... ... @@ -154,6 +198,10 @@ public:
154 198 return "-" + sname + ", --" + lname;
155 199 }
156 200  
  201 + bool check_name(const std::string& name) const {
  202 + return name == sname || name == lname || name == sname + "," + lname;
  203 + }
  204 +
157 205 bool check_sname(const std::string& name) const {
158 206 return name == sname;
159 207 }
... ... @@ -170,24 +218,23 @@ public:
170 218 return lname;
171 219 }
172 220  
173   -
174   - int get_num() const {
175   - return opts.num;
176   - }
177   -
178 221 void add_result(int r, std::string s) {
  222 + logit("Adding result: " + s);
179 223 results.at(r).push_back(s);
180 224 }
181 225 int get_new() {
182 226 results.emplace_back();
183 227 return results.size() - 1;
184 228 }
185   - int count() {
186   - return results.size();
  229 + int count() const {
  230 + int out = 0;
  231 + for(const std::vector<std::string>& v : results)
  232 + out += v.size();
  233 + return out;
187 234 }
188 235  
189 236 std::string string() const {
190   - std::string val = "Option: " + getName() + "\n"
  237 + std::string val = "Option: " + get_name() + "\n"
191 238 + " " + discription + "\n"
192 239 + " [";
193 240 for(const auto& item : results) {
... ... @@ -199,8 +246,58 @@ public:
199 246 return val;
200 247 }
201 248  
  249 + int help_len() const {
  250 + return std::min((int) get_name().length(), 40);
  251 + }
  252 +
  253 + std::string help(int len = 0) const {
  254 + std::stringstream out;
  255 + out << std::setw(len) << std::left << get_name() << discription;
  256 + return out.str();
  257 + }
  258 +
202 259 };
203 260  
  261 +
  262 +template<typename T>
  263 +typename std::enable_if<std::is_integral<T>::value, bool>::type
  264 +lexical_cast(std::string input, T& output) {
  265 + logit("Int lexical cast " + input);
  266 + try{
  267 + output = (T) std::stoll(input);
  268 + return true;
  269 + } catch (std::invalid_argument) {
  270 + return false;
  271 + } catch (std::out_of_range) {
  272 + return false;
  273 + }
  274 +}
  275 +
  276 +template<typename T>
  277 +typename std::enable_if<std::is_floating_point<T>::value, bool>::type
  278 +lexical_cast(std::string input, T& output) {
  279 + logit("Floating lexical cast " + input);
  280 + try{
  281 + output = (T) std::stold(input);
  282 + return true;
  283 + } catch (std::invalid_argument) {
  284 + return false;
  285 + } catch (std::out_of_range) {
  286 + return false;
  287 + }
  288 +}
  289 +
  290 +// String and similar
  291 +template<typename T>
  292 +bool lexical_cast(std::string input, T& output) {
  293 + logit("Direct lexical cast: " + input);
  294 + output = input;
  295 + return true;
  296 +}
  297 +
  298 +enum class Classifer {NONE, POSITIONAL_MARK, SHORT, LONG, SUBCOMMAND};
  299 +
  300 +
204 301 /// Creates a command line program, with very few defaults.
205 302 /** To use, create a new Program() instance with argc, argv, and a help discription. The templated
206 303 * add_option methods make it easy to prepare options. Remember to call `.start` before starting your
... ... @@ -210,19 +307,31 @@ public:
210 307  
211 308 protected:
212 309  
213   - std::string desc;
  310 + std::string name;
  311 + std::string discription;
214 312 std::vector<Option> options;
215 313 std::vector<std::string> missing_options;
216 314 std::vector<std::string> positionals;
  315 + std::vector<App> subcommands;
  316 + bool parsed{false};
  317 + App* subcommand = nullptr;
217 318  
218 319 public:
219 320  
220 321  
221 322 /// Create a new program. Pass in the same arguments as main(), along with a help string.
222   - App(std::string discription)
223   - : desc(discription) {
  323 + App(std::string discription="")
  324 + : discription(discription) {
  325 +
  326 + add_flag("h,help", "Print this help message and exit");
  327 +
  328 + }
  329 +
  330 + App& add_subcommand(std::string name) {
  331 + subcommands.emplace_back();
  332 + subcommands.back().name = name;
  333 + return subcommands.back();
224 334 }
225   -
226 335 /// Add an option, will automatically understand the type for common types.
227 336 /** To use, create a variable with the expected type, and pass it in after the name.
228 337 * After start is called, you can use count to see if the value was passed, and
... ... @@ -242,23 +351,27 @@ public:
242 351 std::string discription="", ///< Discription string
243 352 Combiner opts=ARGS ///< The options (REQUIRED, DEFAULT, POSITIONAL, ARGS())
244 353 ) {
245   -
246 354 Option myopt{name, discription, opts, callback};
247 355 if(std::find(std::begin(options), std::end(options), myopt) == std::end(options))
248 356 options.push_back(myopt);
249 357 else
250   - throw OptionAlreadyAdded(myopt.getName());
  358 + throw OptionAlreadyAdded(myopt.get_name());
251 359  
252 360 }
253 361  
254 362 /// Add option for string
255   - void add_option(
  363 + template<typename T>
  364 + typename std::enable_if<!std::is_array<T>::value, void>::type
  365 + add_option(
256 366 std::string name, ///< The name, long,short
257   - std::string &variable, ///< The variable to set
  367 + T &variable, ///< The variable to set
258 368 std::string discription="", ///< Discription string
259 369 Combiner opts=ARGS ///< The options (REQUIRED, DEFAULT, POSITIONAL, ARGS())
260 370 ) {
261 371  
  372 +
  373 + if(opts.num!=1)
  374 + throw IncorrectConstruction("Must have ARGS(1) or be a vector.");
262 375 CLI::callback_t fun = [&variable](CLI::results_t res){
263 376 if(res.size()!=1) {
264 377 return false;
... ... @@ -266,8 +379,33 @@ public:
266 379 if(res[0].size()!=1) {
267 380 return false;
268 381 }
269   - variable = res[0][0];
270   - return true;
  382 + return lexical_cast(res[0][0], variable);
  383 + };
  384 +
  385 + add_option(name, fun, discription, opts);
  386 + }
  387 +
  388 + /// Add option for vector of results
  389 + template<typename T>
  390 + void add_option(
  391 + std::string name, ///< The name, long,short
  392 + std::vector<T> &variable, ///< The variable to set
  393 + std::string discription="", ///< Discription string
  394 + Combiner opts=ARGS ///< The options (REQUIRED, DEFAULT, POSITIONAL, ARGS())
  395 + ) {
  396 +
  397 + if(opts.num==0)
  398 + throw IncorrectConstruction("Must have ARGS(1) or be a vector.");
  399 + CLI::callback_t fun = [&variable](CLI::results_t res){
  400 + bool retval = true;
  401 + int count = 0;
  402 + variable.clear();
  403 + for(const auto &a : res)
  404 + for(const auto &b : a) {
  405 + variable.emplace_back();
  406 + retval &= lexical_cast(b, variable.back());
  407 + }
  408 + return count != 0 && retval;
271 409 };
272 410  
273 411 add_option(name, fun, discription, opts);
... ... @@ -279,7 +417,6 @@ public:
279 417 std::string name, ///< The name, short,long
280 418 std::string discription="" ///< Discription string
281 419 ) {
282   -
283 420 CLI::callback_t fun = [](CLI::results_t res){
284 421 return true;
285 422 };
... ... @@ -298,141 +435,244 @@ public:
298 435  
299 436 count = 0;
300 437 CLI::callback_t fun = [&count](CLI::results_t res){
301   - count = res.size();
  438 + count = (T) res.size();
302 439 return true;
303 440 };
304 441  
305 442 add_option(name, fun, discription, NOTHING);
306 443 }
  444 +
  445 + /// Add set of options
  446 + template<typename T>
  447 + void add_set(
  448 + std::string name, ///< The name, short,long
  449 + T &member, ///< The selected member of the set
  450 + std::unordered_set<T> options, ///< The set of posibilities
  451 + std::string discription="", ///< Discription string
  452 + Combiner opts=ARGS ///< The options (REQUIRED, DEFAULT, POSITIONAL, ARGS())
  453 + ) {
  454 +
  455 + CLI::callback_t fun = [&member, options](CLI::results_t res){
  456 + if(res.size()!=1) {
  457 + return false;
  458 + }
  459 + if(res[0].size()!=1) {
  460 + return false;
  461 + }
  462 + bool retval = lexical_cast(res[0][0], member);
  463 + if(!retval)
  464 + return false;
  465 + return std::find(std::begin(options), std::end(options), retval) != std::end(options);
  466 + };
  467 +
  468 + add_option(name, fun, discription, opts);
  469 + }
  470 +
307 471  
308 472  
309 473 /// Parses the command line - throws errors
310   - void parse(int argc, char **argv) throw(CallForHelp, ParseError) {
  474 + void parse(int argc, char **argv) {
311 475 std::vector<std::string> args;
312 476 for(int i=1; i<argc; i++)
313 477 args.emplace_back(argv[i]);
314 478 parse(args);
315 479 }
316 480  
317   - void parse(std::vector<std::string> args) throw(CallForHelp, ParseError) {
  481 + void parse(std::vector<std::string> args) {
  482 + parsed = true;
318 483 std::reverse(args.begin(), args.end());
319 484  
320 485 bool positional_only = false;
321 486  
322 487 while(args.size()>0) {
323 488  
324   - if(args.back() == "--") {
  489 + logit(join(args));
  490 +
  491 + Classifer classifer = positional_only ? Classifer::NONE : _recognize(args.back());
  492 + switch(classifer) {
  493 + case Classifer::POSITIONAL_MARK:
325 494 args.pop_back();
326 495 positional_only = true;
327   - } else if(positional_only || (!_parse_long(args) && !_parse_short(args))) {
328   -
  496 + break;
  497 + case Classifer::SUBCOMMAND:
  498 + _parse_subcommand(args);
  499 + break;
  500 + case Classifer::LONG:
  501 + _parse_long(args);
  502 + break;
  503 + case Classifer::SHORT:
  504 + _parse_short(args);
  505 + break;
  506 + case Classifer::NONE:
329 507 logit("Positional: "+args.back());
330 508 positionals.push_back(args.back());
331 509 args.pop_back();
332 510 }
333 511 }
334 512  
335   - for(Option& opt : options)
  513 + if (count("help") > 0) {
  514 + throw CallForHelp();
  515 + }
  516 +
  517 + bool success = true;
  518 + for(Option& opt : options) {
  519 + while (opt.positional() && opt.count() < opt.expected() && positionals.size() > 0) {
  520 + opt.get_new();
  521 + opt.add_result(0, positionals.back());
  522 + positionals.pop_back();
  523 + }
  524 + if (opt.required() && opt.count() < opt.expected())
  525 + throw RequiredError(opt.get_name());
336 526 if (opt.count() > 0) {
337   - logit(opt.string());
338   - if(!opt.run_callback())
339   - logit("Failed");
  527 + success &= opt.run_callback();
340 528 }
341 529  
342   - //TODO: Check for false callbacks
  530 + }
  531 + if(!success)
  532 + throw ParseError();
  533 + if(positionals.size()>0)
  534 + throw ExtraPositionalsError();
  535 + }
  536 +
  537 + void _parse_subcommand(std::vector<std::string> &args) {
  538 + for(App &com : subcommands) {
  539 + if(com.name == args.back()){
  540 + args.pop_back();
  541 + com.parse(args);
  542 + subcommand = &com;
  543 + return;
  544 + }
  545 + }
  546 + throw HorribleError("Subcommand");
343 547 }
344 548  
345   - bool _parse_short(std::vector<std::string> &args) {
  549 + void _parse_short(std::vector<std::string> &args) {
346 550 std::string current = args.back();
347 551 std::smatch match;
348 552  
349 553 if(!std::regex_match(current, match, reg_short))
350   - return false;
  554 + throw HorribleError("Subcommand");
351 555  
352 556 args.pop_back();
353 557 std::string name = match[1];
354 558 std::string rest = match[2];
355 559  
356   - logit("Working on short:");
357   - logit(name);
358   - logit(rest);
  560 + logit("Working on short: " + name + " (" + rest + ")");
359 561  
360 562 auto op = std::find_if(std::begin(options), std::end(options), [name](const Option &v){return v.check_sname(name);});
361 563  
362 564 if(op == std::end(options)) {
363 565 missing_options.push_back("-" + op->get_sname());
364   - return true;
  566 + return;
365 567 }
366 568  
367 569 int vnum = op->get_new();
368   - int num = op->get_num();
369   -
370   -
371   - if(rest != "" && num > 0) {
372   - num--;
  570 + int num = op->expected();
  571 +
  572 + if(num == 0)
  573 + op->add_result(vnum, "");
  574 + else if(rest!="") {
  575 + if (num > 0)
  576 + num--;
373 577 op->add_result(vnum, rest);
374 578 rest = "";
375 579 }
376 580  
377   - while(num>0) {
  581 +
  582 + if(num == -1) {
  583 + std::string current = args.back();
  584 + while(_recognize(current) == Classifer::NONE) {
  585 + std::string current = args.back();
  586 + args.pop_back();
  587 + op->add_result(vnum,current);
  588 + if(args.size()==0)
  589 + return;
  590 +
  591 + }
  592 + } else while(num>0) {
378 593 num--;
379 594 std::string current = args.back();
380 595 logit("Adding: "+current);
381 596 args.pop_back();
382 597 op->add_result(vnum,current);
383 598 if(args.size()==0)
384   - return true;
  599 + return;
385 600 }
386 601  
387 602 if(rest != "") {
388 603 rest = "-" + rest;
389 604 args.push_back(rest);
390 605 }
391   - return true;
392 606 }
393 607  
394   - bool _parse_long(std::vector<std::string> &args) {
  608 + Classifer _recognize(std::string current) const {
  609 + if(current == "--")
  610 + return Classifer::POSITIONAL_MARK;
  611 + for(const App &com : subcommands) {
  612 + if(com.name == current)
  613 + return Classifer::SUBCOMMAND;
  614 + }
  615 + if(std::regex_match(current, reg_long))
  616 + return Classifer::LONG;
  617 + if(std::regex_match(current, reg_short))
  618 + return Classifer::SHORT;
  619 + return Classifer::NONE;
  620 +
  621 +
  622 + }
  623 +
  624 + void _parse_long(std::vector<std::string> &args) {
395 625 std::string current = args.back();
396 626 std::smatch match;
397 627  
398 628 if(!std::regex_match(current, match, reg_long))
399   - return false;
400   -
  629 + throw HorribleError("Long");
  630 +
401 631 args.pop_back();
402 632 std::string name = match[1];
403 633 std::string value = match[2];
404 634  
405 635  
406   - logit("Working on long:");
407   - logit(name);
408   - logit(value);
  636 + logit("Working on long: " + name + " (" + value + ")");
409 637  
410 638 auto op = std::find_if(std::begin(options), std::end(options), [name](const Option &v){return v.check_lname(name);});
411 639  
412 640 if(op == std::end(options)) {
413 641 missing_options.push_back("--" + op->get_lname());
414   - return true;
  642 + return;
415 643 }
416 644  
417 645  
418 646 int vnum = op->get_new();
419   - int num = op->get_num();
  647 + int num = op->expected();
420 648  
421 649  
422 650 if(value != "") {
423   - num--;
  651 + if(num!=-1) num--;
424 652 op->add_result(vnum, value);
  653 + } else if (num == 0) {
  654 + op->add_result(vnum, "");
425 655 }
426 656  
427   - while(num>0) {
  657 + if(num == -1) {
  658 + std::string current = args.back();
  659 + while(_recognize(current) == Classifer::NONE) {
  660 + std::string current = args.back();
  661 + args.pop_back();
  662 + op->add_result(vnum,current);
  663 + if(args.size()==0)
  664 + return;
  665 +
  666 + }
  667 + } else while(num>0) {
428 668 num--;
429 669 std::string current = args.back();
430 670 args.pop_back();
431 671 op->add_result(vnum,current);
432 672 if(args.size()==0)
433   - return true;
  673 + return;
434 674 }
435   - return true;
  675 + return;
436 676 }
437 677  
438 678 /// This must be called after the options are in but before the rest of the program.
... ... @@ -444,7 +684,7 @@ public:
444 684 } catch(const CallForHelp &e) {
445 685 std::cout << help() << std::endl;
446 686 exit(0);
447   - } catch(const ParseError &e) {
  687 + } catch(const Error &e) {
448 688 std::cerr << "ERROR:" << std::endl;
449 689 std::cerr << e.what() << std::endl;
450 690 std::cerr << help() << std::endl;
... ... @@ -455,11 +695,43 @@ public:
455 695  
456 696 /// Counts the number of times the given option was passed.
457 697 int count(std::string name) const {
458   - return 0;
  698 + for(const Option &opt : options) {
  699 + logit("Computing: " + opt.get_name());
  700 + if(opt.check_name(name)) {
  701 + logit("Counting: " + opt.get_name() + std::to_string(opt.count()));
  702 + return opt.count();
  703 + }
  704 + }
  705 + throw OptionNotFound(name);
459 706 }
460 707  
461   - std::string help() const {return "";}
  708 + std::string help() const {
  709 + std::stringstream out;
  710 + out << discription << std::endl;
  711 + int len = std::accumulate(std::begin(options), std::end(options), 0,
  712 + [](int val, const Option &opt){
  713 + return std::max(opt.help_len(), val);});
  714 + for(const Option &opt : options) {
  715 + out << opt.help(len) << std::endl;
  716 + }
  717 + 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 + }
  724 + out << std::endl;
  725 + }
  726 + return out.str();
  727 + }
462 728  
  729 + App* get_subcommand() {
  730 + return subcommand;
  731 + }
463 732  
  733 + std::string get_name() const {
  734 + return name;
  735 + }
464 736 };
465 737 }
... ...
... ... @@ -9,12 +9,12 @@ int main (int argc, char** argv) {
9 9 app.add_option("f,file", file, "File name");
10 10  
11 11 int count;
12   - app.add_flag<int>("c,count", count, "File name");
  12 + app.add_flag("c,count", count, "File name");
13 13  
14   - app.parse(argc, argv);
  14 + app.start(argc, argv);
15 15  
16   - std::cout << "Working on file: " << file << std::endl;
17   - std::cout << "Working on count: " << count << std::endl;
  16 + std::cout << "Working on file: " << file << ", direct count: " << app.count("file") << std::endl;
  17 + std::cout << "Working on count: " << count << ", direct count: " << app.count("count") << std::endl;
18 18  
19 19 return 0;
20 20 }
... ...