Commit a12a94c4c1ac34836a4a33646b1b5948901ed854

Authored by Henry Fredrick Schreiner
1 parent 44a27be5

Separate files, plus way to combine

examples/try.cpp
1   -#include "CLI.hpp"
  1 +#include "CLI/CLI.hpp"
2 2  
3 3  
4 4 int main (int argc, char** argv) {
... ...
examples/try1.cpp
1   -#include "CLI.hpp"
  1 +#include "CLI/CLI.hpp"
2 2  
3 3  
4 4 int main (int argc, char** argv) {
... ...
include/CLI.hpp renamed to include/CLI/App.hpp
... ... @@ -8,628 +8,26 @@
8 8 #include <deque>
9 9 #include <iostream>
10 10 #include <functional>
11   -#include <tuple>
12   -#include <exception>
13   -#include <stdexcept>
14 11 #include <algorithm>
15 12 #include <sstream>
16   -#include <type_traits>
17 13 #include <set>
18   -#include <iomanip>
19 14 #include <numeric>
20 15 #include <vector>
21   -#include <locale>
22 16  
23   -// C standard library
24   -// Only needed for existence checking
25   -// Could be swapped for filesystem in C++17
26   -#include <sys/types.h>
27   -#include <sys/stat.h>
28 17  
  18 +// CLI Library includes
  19 +#include "CLI/Error.hpp"
  20 +#include "CLI/TypeTools.hpp"
  21 +#include "CLI/StringTools.hpp"
  22 +#include "CLI/Split.hpp"
  23 +#include "CLI/Combiner.hpp"
  24 +#include "CLI/Option.hpp"
  25 +#include "CLI/Value.hpp"
29 26  
30 27 namespace CLI {
31 28  
32   -
33   -// Error definitions
34   -
35   -struct Error : public std::runtime_error {
36   - int exit_code;
37   - bool print_help;
38   - Error(std::string parent, std::string name, int exit_code=255, bool print_help=true) : runtime_error(parent + ": " + name), exit_code(exit_code), print_help(print_help) {}
39   -};
40   -
41   -struct Success : public Error {
42   - Success() : Error("Success", "Successfully completed, should be caught and quit", 0, false) {}
43   -};
44   -
45   -struct CallForHelp : public Error {
46   - CallForHelp() : Error("CallForHelp", "This should be caught in your main function, see examples", 0) {}
47   -};
48   -
49   -struct BadNameString : public Error {
50   - BadNameString(std::string name) : Error("BadNameString", name, 1) {}
51   -};
52   -
53   -
54   -struct ParseError : public Error {
55   - ParseError(std::string name) : Error("ParseError", name, 2) {}
56   -};
57   -
58   -struct OptionAlreadyAdded : public Error {
59   - OptionAlreadyAdded(std::string name) : Error("OptionAlreadyAdded", name, 3) {}
60   -};
61   -
62   -struct OptionNotFound : public Error {
63   - OptionNotFound(std::string name) : Error("OptionNotFound", name, 4) {}
64   -};
65   -
66   -struct RequiredError : public Error {
67   - RequiredError(std::string name) : Error("RequiredError", name, 5) {}
68   -};
69   -
70   -struct PositionalError : public Error {
71   - PositionalError(std::string name) : Error("PositionalError", name, 6) {}
72   -};
73   -
74   -struct HorribleError : public Error {
75   - HorribleError(std::string name) : Error("HorribleError", "(You should never see this error) " + name, 7) {}
76   -};
77   -struct IncorrectConstruction : public Error {
78   - IncorrectConstruction(std::string name) : Error("IncorrectConstruction", name, 8) {}
79   -};
80   -struct EmptyError : public Error {
81   - EmptyError(std::string name) : Error("EmptyError", name, 9) {}
82   -};
83   -
84   -
85   -// Type tools
86   -//
87   -// Copied from C++14
88   -#if __cplusplus < 201402L
89   -template< bool B, class T = void >
90   -using enable_if_t = typename std::enable_if<B,T>::type;
91   -#else
92   -using std::enable_if_t;
93   -#endif
94   -// If your compiler supports C++14, you can use that definition instead
95   -
96   -template <typename T>
97   -struct is_vector {
98   - static const bool value = false;
99   -};
100   -
101   -
102   -template<class T, class A>
103   -struct is_vector<std::vector<T, A> > {
104   - static bool const value = true;
105   -};
106   -
107   -template <typename T>
108   -struct is_bool {
109   - static const bool value = false;
110   -};
111   -
112   -template<>
113   -struct is_bool<bool> {
114   - static bool const value = true;
115   -};
116   -
117   -namespace detail {
118   - // Based generally on https://rmf.io/cxx11/almost-static-if
119   - /// Simple empty scoped class
120   - enum class enabler {};
121   -
122   - /// An instance to use in EnableIf
123   - constexpr enabler dummy = {};
124   -
125   - /// Simple function to join a string
126   - template <typename T>
127   - std::string join(const T& v, std::string delim = ",") {
128   - std::ostringstream s;
129   - size_t start = 0;
130   - for (const auto& i : v) {
131   - if(start++ > 0)
132   - s << delim;
133   - s << i;
134   - }
135   - return s.str();
136   - }
137   -
138   - /// Was going to be based on
139   - /// http://stackoverflow.com/questions/1055452/c-get-name-of-type-in-template
140   - /// But this is cleaner and works better in this case
141   -
142   - template<typename T,
143   - enable_if_t<std::is_integral<T>::value && std::is_signed<T>::value, detail::enabler> = detail::dummy>
144   - constexpr const char* type_name() {
145   - return "INT";
146   - }
147   -
148   - template<typename T,
149   - enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value, detail::enabler> = detail::dummy>
150   - constexpr const char* type_name() {
151   - return "UINT";
152   - }
153   -
154   -
155   - template<typename T,
156   - enable_if_t<std::is_floating_point<T>::value, detail::enabler> = detail::dummy>
157   - constexpr const char* type_name() {
158   - return "FLOAT";
159   - }
160   -
161   -
162   - /// This one should not be used, since vector types print the internal type
163   - template<typename T,
164   - enable_if_t<is_vector<T>::value, detail::enabler> = detail::dummy>
165   - constexpr const char* type_name() {
166   - return "VECTOR";
167   - }
168   -
169   -
170   - template<typename T,
171   - enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value && !is_vector<T>::value
172   - , detail::enabler> = detail::dummy>
173   - constexpr const char* type_name() {
174   - return "STRING";
175   - }
176   -
177   - void format_help(std::stringstream &out, std::string name, std::string description, size_t wid) {
178   - name = " " + name;
179   - out << std::setw(wid) << std::left << name;
180   - if(description != "") {
181   - if(name.length()>=wid)
182   - out << std::endl << std::setw(wid) << "";
183   - out << description << std::endl;
184   - }
185   - }
186   -
187   - struct Combiner {
188   - int num;
189   - bool required;
190   - bool defaulted;
191   - std::vector<std::function<bool(std::string)>> validators;
192   -
193   - /// Can be or-ed together
194   - Combiner operator | (Combiner b) const {
195   - Combiner self;
196   - self.num = std::min(num, b.num) == -1 ? -1 : std::max(num, b.num);
197   - self.required = required || b.required;
198   - self.defaulted = defaulted || b.defaulted;
199   - self.validators.reserve(validators.size() + b.validators.size());
200   - self.validators.insert(self.validators.end(), validators.begin(), validators.end());
201   - self.validators.insert(self.validators.end(), b.validators.begin(), b.validators.end());
202   - return self;
203   - }
204   -
205   - /// Call to give the number of arguments expected on cli
206   - Combiner operator() (int n) const {
207   - Combiner self = *this;
208   - self.num = n;
209   - return self;
210   - }
211   - /// Call to give a validator
212   - Combiner operator() (std::function<bool(std::string)> func) const {
213   - Combiner self = *this;
214   - self.validators.push_back(func);
215   - return self;
216   - }
217   - };
218   -
219   - bool _ExistingFile(std::string filename) {
220   - // std::fstream f(name.c_str());
221   - // return f.good();
222   - // Fastest way according to http://stackoverflow.com/questions/12774207/fastest-way-to-check-if-a-file-exist-using-standard-c-c11-c
223   - struct stat buffer;
224   - return (stat(filename.c_str(), &buffer) == 0);
225   - }
226   -
227   - bool _ExistingDirectory(std::string filename) {
228   - struct stat buffer;
229   - if(stat(filename.c_str(), &buffer) == 0 && (buffer.st_mode & S_IFDIR) )
230   - return true;
231   - return false;
232   - }
233   -
234   - bool _NonexistentPath(std::string filename) {
235   - struct stat buffer;
236   - return stat(filename.c_str(), &buffer) != 0;
237   - }
238   -
239   -
240   - template<typename T>
241   - bool valid_first_char(T c) {
242   - return std::isalpha(c) || c=='_';
243   - }
244   -
245   - template<typename T>
246   - bool valid_later_char(T c) {
247   - return std::isalnum(c) || c=='_' || c=='.' || c=='-';
248   - }
249   -
250   - inline bool valid_name_string(const std::string &str) {
251   - if(str.size()<1 || !valid_first_char(str[0]))
252   - return false;
253   - for(auto c : str.substr(1))
254   - if(!valid_later_char(c))
255   - return false;
256   - return true;
257   - }
258   -
259   - // Returns false if not a short option. Otherwise, sets opt name and rest and returns true
260   - inline bool split_short(const std::string &current, std::string &name, std::string &rest) {
261   - if(current.size()>1 && current[0] == '-' && valid_first_char(current[1])) {
262   - name = current.substr(1,1);
263   - rest = current.substr(2);
264   - return true;
265   - } else
266   - return false;
267   - }
268   -
269   - // Returns false if not a long option. Otherwise, sets opt name and other side of = and returns true
270   - inline bool split_long(const std::string &current, std::string &name, std::string &value) {
271   - if(current.size()>2 && current.substr(0,2) == "--" && valid_first_char(current[2])) {
272   - auto loc = current.find("=");
273   - if(loc != std::string::npos) {
274   - name = current.substr(2,loc-2);
275   - value = current.substr(loc+1);
276   - } else {
277   - name = current.substr(2);
278   - value = "";
279   - }
280   - return true;
281   - } else
282   - return false;
283   - }
284   -
285   - // Splits a string into multiple long and short names
286   - inline std::vector<std::string> split_names(std::string current) {
287   - std::vector<std::string> output;
288   - size_t val;
289   - while((val = current.find(",")) != std::string::npos) {
290   - output.push_back(current.substr(0,val));
291   - current = current.substr(val+1);
292   - }
293   - output.push_back(current);
294   - return output;
295   -
296   - }
297   -
298   -
299   - inline std::tuple<std::vector<std::string>,std::vector<std::string>, std::string>
300   - get_names(const std::vector<std::string> &input) {
301   -
302   - std::vector<std::string> short_names;
303   - std::vector<std::string> long_names;
304   - std::string pos_name;
305   -
306   - for(std::string name : input) {
307   - if(name.length() == 0)
308   - continue;
309   - else if(name.length() > 1 && name[0] == '-' && name[1] != '-') {
310   - if(name.length()==2 && valid_first_char(name[1]))
311   - short_names.push_back(std::string(1,name[1]));
312   - else
313   - throw BadNameString("Invalid one char name: "+name);
314   - } else if(name.length() > 2 && name.substr(0,2) == "--") {
315   - name = name.substr(2);
316   - if(valid_name_string(name))
317   - long_names.push_back(name);
318   - else
319   - throw BadNameString("Bad long name: "+name);
320   - } else if(name == "-" || name == "--") {
321   - throw BadNameString("Must have a name, not just dashes");
322   - } else {
323   - if(pos_name.length() > 0)
324   - throw BadNameString("Only one positional name allowed, remove: "+name);
325   - pos_name = name;
326   -
327   - }
328   - }
329   -
330   - return std::tuple<std::vector<std::string>,std::vector<std::string>, std::string>
331   - (short_names, long_names, pos_name);
332   - }
333   -
334   - // Integers
335   - template<typename T, enable_if_t<std::is_integral<T>::value, detail::enabler> = detail::dummy>
336   - bool lexical_cast(std::string input, T& output) {
337   - try{
338   - output = (T) std::stoll(input);
339   - return true;
340   - } catch (std::invalid_argument) {
341   - return false;
342   - } catch (std::out_of_range) {
343   - return false;
344   - }
345   - }
346   -
347   - // Floats
348   - template<typename T, enable_if_t<std::is_floating_point<T>::value, detail::enabler> = detail::dummy>
349   - bool lexical_cast(std::string input, T& output) {
350   - try{
351   - output = (T) std::stold(input);
352   - return true;
353   - } catch (std::invalid_argument) {
354   - return false;
355   - } catch (std::out_of_range) {
356   - return false;
357   - }
358   - }
359   -
360   - // Vector
361   - template<typename T,
362   - enable_if_t<is_vector<T>::value, detail::enabler> = detail::dummy>
363   - bool lexical_cast(std::string input, T& output) {
364   - if(output.size() == input.size())
365   - output.resize(input.size());
366   - for(size_t i=0; i<input.size(); i++)
367   - output[i] = input[i];
368   - return true;
369   - }
370   -
371   - // String and similar
372   - template<typename T,
373   - enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value && !is_vector<T>::value
374   - , detail::enabler> = detail::dummy>
375   - bool lexical_cast(std::string input, T& output) {
376   - output = input;
377   - return true;
378   - }
379   -}
380   -
381   -
382   -
383   -// Defines for common Combiners (don't use combiners directly)
384   -
385   -const detail::Combiner Nothing {0, false, false, {}};
386   -const detail::Combiner Required {1, true, false, {}};
387   -const detail::Combiner Default {1, false, true, {}};
388   -const detail::Combiner Args {-1, false, false, {}};
389   -const detail::Combiner Validators {1, false, false, {}};
390   -
391   -// Warning about using these validators:
392   -// The files could be added/deleted after the validation. This is not common,
393   -// but if this is a possibility, check the file you open afterwards
394   -const detail::Combiner ExistingFile {1, false, false, {detail::_ExistingFile}};
395   -const detail::Combiner ExistingDirectory {1, false, false, {detail::_ExistingDirectory}};
396   -const detail::Combiner NonexistentPath {1, false, false, {detail::_NonexistentPath}};
397   -
398   -typedef std::vector<std::vector<std::string>> results_t;
399   -typedef std::function<bool(results_t)> callback_t;
400   -
401   -
402   -class App;
403   -
404   -class Option {
405   - friend App;
406   -protected:
407   - // Config
408   - std::vector<std::string> snames;
409   - std::vector<std::string> lnames;
410   - std::string pname;
411   -
412   - detail::Combiner opts;
413   - std::string description;
414   - callback_t callback;
415   -
416   - // These are for help strings
417   - std::string defaultval;
418   - std::string typeval;
419   -
420   - // Results
421   - results_t results {};
422   -
423   -
424   -public:
425   - Option(std::string name, std::string description = "", detail::Combiner opts=Nothing, std::function<bool(results_t)> callback=[](results_t){return true;}) :
426   - opts(opts), description(description), callback(callback){
427   - std::tie(snames, lnames, pname) = detail::get_names(detail::split_names(name));
428   - }
429   -
430   - /// Clear the parsed results (mostly for testing)
431   - void clear() {
432   - results.clear();
433   - }
434   -
435   - /// True if option is required
436   - bool required() const {
437   - return opts.required;
438   - }
439   -
440   - /// The number of arguments the option expects
441   - int expected() const {
442   - return opts.num;
443   - }
444   -
445   - /// True if the argument can be given directly
446   - bool positional() const {
447   - return pname.length() > 0;
448   - }
449   -
450   - /// True if option has at least one non-positional name
451   - bool nonpositional() const {
452   - return (snames.size() + lnames.size()) > 0;
453   - }
454   -
455   - /// True if this should print the default string
456   - bool defaulted() const {
457   - return opts.defaulted;
458   - }
459   -
460   - /// True if option has description
461   - bool has_description() const {
462   - return description.length() > 0;
463   - }
464   -
465   - /// Get the description
466   - const std::string& get_description() const {
467   - return description;
468   - }
469   -
470   - /// The name and any extras needed for positionals
471   - std::string help_positional() const {
472   - std::string out = pname;
473   - if(expected()<1)
474   - out = out + "x" + std::to_string(expected());
475   - else if(expected()==-1)
476   - out = out + "...";
477   - out = required() ? out : "["+out+"]";
478   - return out;
479   - }
480   -
481   - // Just the pname
482   - std::string get_pname() const {
483   - return pname;
484   - }
485   -
486   - /// Process the callback
487   - bool run_callback() const {
488   - if(opts.validators.size()>0) {
489   - for(const std::string & result : flatten_results())
490   - for(const std::function<bool(std::string)> &vali : opts.validators)
491   - if(!vali(result))
492   - return false;
493   - }
494   - return callback(results);
495   - }
496   -
497   - /// If options share any of the same names, they are equal (not counting positional)
498   - bool operator== (const Option& other) const {
499   - for(const std::string &sname : snames)
500   - for(const std::string &othersname : other.snames)
501   - if(sname == othersname)
502   - return true;
503   - for(const std::string &lname : lnames)
504   - for(const std::string &otherlname : other.lnames)
505   - if(lname == otherlname)
506   - return true;
507   - return false;
508   - }
509   -
510   - /// Gets a , sep list of names. Does not include the positional name.
511   - std::string get_name() const {
512   - std::vector<std::string> name_list;
513   - for(const std::string& sname : snames)
514   - name_list.push_back("-"+sname);
515   - for(const std::string& lname : lnames)
516   - name_list.push_back("--"+lname);
517   - return detail::join(name_list);
518   - }
519   -
520   - /// Check a name. Requires "-" or "--" for short / long, supports positional name
521   - bool check_name(std::string name) const {
522   -
523   - if(name.length()>2 && name.substr(0,2) == "--")
524   - return check_lname(name.substr(2));
525   - else if (name.length()>1 && name.substr(0,1) == "-")
526   - return check_sname(name.substr(1));
527   - else
528   - return name == pname;
529   - }
530   -
531   - /// Requires "-" to be removed from string
532   - bool check_sname(const std::string& name) const {
533   - return std::find(std::begin(snames), std::end(snames), name) != std::end(snames);
534   - }
535   -
536   - /// Requires "--" to be removed from string
537   - bool check_lname(const std::string& name) const {
538   - return std::find(std::begin(lnames), std::end(lnames), name) != std::end(lnames);
539   - }
540   -
541   -
542   - /// Puts a result at position r
543   - void add_result(int r, std::string s) {
544   - results.at(r).push_back(s);
545   - }
546   -
547   - /// Starts a new results vector (used for r in add_result)
548   - int get_new() {
549   - results.emplace_back();
550   - return results.size() - 1;
551   - }
552   -
553   - /// Count the total number of times an option was passed
554   - int count() const {
555   - int out = 0;
556   - for(const std::vector<std::string>& v : results)
557   - out += v.size();
558   - return out;
559   - }
560   -
561   - /// Diagnostic representation
562   - std::string string() const {
563   - std::string val = "Option: " + get_name() + "\n"
564   - + " " + description + "\n"
565   - + " [";
566   - for(const auto& item : results) {
567   - if(&item!=&results[0])
568   - val+="],[";
569   - val += detail::join(item);
570   - }
571   - val += "]";
572   - return val;
573   - }
574   -
575   - /// The first half of the help print, name plus default, etc
576   - std::string help_name() const {
577   - std::stringstream out;
578   - out << get_name();
579   - if(expected() != 0) {
580   - if(typeval != "")
581   - out << " " << typeval;
582   - if(defaultval != "")
583   - out << "=" << defaultval;
584   - if(expected() > 1)
585   - out << " x " << expected();
586   - if(expected() == -1)
587   - out << " ...";
588   - }
589   - return out.str();
590   - }
591   -
592   - /// Produce a flattened vector of results, vs. a vector of vectors.
593   - std::vector<std::string> flatten_results() const {
594   - std::vector<std::string> output;
595   - for(const std::vector<std::string> result : results)
596   - output.insert(std::end(output), std::begin(result), std::end(result));
597   - return output;
598   - }
599   -
600   -};
601   -
602   -
603   -
604 29 enum class Classifer {NONE, POSITIONAL_MARK, SHORT, LONG, SUBCOMMAND};
605 30  
606   -
607   -// Prototype return value test
608   -template <typename T>
609   -class Value {
610   - friend App;
611   -protected:
612   - std::shared_ptr<std::unique_ptr<T>> value {new std::unique_ptr<T>()};
613   - std::string name;
614   -public:
615   - Value(std::string name) : name(name) {}
616   -
617   - operator bool() const {return (bool) *value;}
618   -
619   - T& get() const {
620   - if(*value)
621   - return **value;
622   - else
623   - throw EmptyError(name);
624   - }
625   - /// Note this does not throw on assignment, though
626   - /// afterwards it seems to work fine. Best to use
627   - /// explicit * notation.
628   - T& operator *() const {
629   - return get();
630   - }
631   -};
632   -
633 31 /// Creates a command line program, with very few defaults.
634 32 /** To use, create a new Program() instance with argc, argv, and a help description. The templated
635 33 * add_option methods make it easy to prepare options. Remember to call `.start` before starting your
... ... @@ -1391,4 +789,6 @@ public:
1391 789 return name;
1392 790 }
1393 791 };
  792 +
  793 +
1394 794 }
... ...
include/CLI/CLI.hpp 0 → 100644
  1 +#pragma once
  2 +
  3 +// Distributed under the LGPL version 3.0 license. See accompanying
  4 +// file LICENSE or https://github.com/henryiii/CLI11 for details.
  5 +
  6 +// CLI Library includes
  7 +#include "CLI/Error.hpp"
  8 +#include "CLI/TypeTools.hpp"
  9 +#include "CLI/StringTools.hpp"
  10 +#include "CLI/Split.hpp"
  11 +#include "CLI/Combiner.hpp"
  12 +#include "CLI/Option.hpp"
  13 +#include "CLI/Value.hpp"
  14 +#include "CLI/App.hpp"
  15 +
... ...
include/CLI/Combiner.hpp 0 → 100644
  1 +#pragma once
  2 +
  3 +// Distributed under the LGPL version 3.0 license. See accompanying
  4 +// file LICENSE or https://github.com/henryiii/CLI11 for details.
  5 +
  6 +#include <string>
  7 +#include <functional>
  8 +#include <vector>
  9 +
  10 +
  11 +// C standard library
  12 +// Only needed for existence checking
  13 +// Could be swapped for filesystem in C++17
  14 +#include <sys/types.h>
  15 +#include <sys/stat.h>
  16 +
  17 +namespace CLI {
  18 +
  19 +namespace detail {
  20 +
  21 +struct Combiner {
  22 + int num;
  23 + bool required;
  24 + bool defaulted;
  25 + std::vector<std::function<bool(std::string)>> validators;
  26 +
  27 + /// Can be or-ed together
  28 + Combiner operator | (Combiner b) const {
  29 + Combiner self;
  30 + self.num = std::min(num, b.num) == -1 ? -1 : std::max(num, b.num);
  31 + self.required = required || b.required;
  32 + self.defaulted = defaulted || b.defaulted;
  33 + self.validators.reserve(validators.size() + b.validators.size());
  34 + self.validators.insert(self.validators.end(), validators.begin(), validators.end());
  35 + self.validators.insert(self.validators.end(), b.validators.begin(), b.validators.end());
  36 + return self;
  37 + }
  38 +
  39 + /// Call to give the number of arguments expected on cli
  40 + Combiner operator() (int n) const {
  41 + Combiner self = *this;
  42 + self.num = n;
  43 + return self;
  44 + }
  45 + /// Call to give a validator
  46 + Combiner operator() (std::function<bool(std::string)> func) const {
  47 + Combiner self = *this;
  48 + self.validators.push_back(func);
  49 + return self;
  50 + }
  51 +};
  52 +
  53 +/// Check for an existing file
  54 +bool _ExistingFile(std::string filename) {
  55 +// std::fstream f(name.c_str());
  56 +// return f.good();
  57 +// Fastest way according to http://stackoverflow.com/questions/12774207/fastest-way-to-check-if-a-file-exist-using-standard-c-c11-c
  58 + struct stat buffer;
  59 + return (stat(filename.c_str(), &buffer) == 0);
  60 +}
  61 +
  62 +/// Check for an existing directory
  63 +bool _ExistingDirectory(std::string filename) {
  64 + struct stat buffer;
  65 + if(stat(filename.c_str(), &buffer) == 0 && (buffer.st_mode & S_IFDIR) )
  66 + return true;
  67 + return false;
  68 +}
  69 +
  70 +/// Check for a non-existing path
  71 +bool _NonexistentPath(std::string filename) {
  72 + struct stat buffer;
  73 + return stat(filename.c_str(), &buffer) != 0;
  74 +}
  75 +
  76 +
  77 +
  78 +
  79 +}
  80 +
  81 +
  82 +
  83 +// Defines for common Combiners (don't use combiners directly)
  84 +
  85 +const detail::Combiner Nothing {0, false, false, {}};
  86 +const detail::Combiner Required {1, true, false, {}};
  87 +const detail::Combiner Default {1, false, true, {}};
  88 +const detail::Combiner Args {-1, false, false, {}};
  89 +const detail::Combiner Validators {1, false, false, {}};
  90 +
  91 +// Warning about using these validators:
  92 +// The files could be added/deleted after the validation. This is not common,
  93 +// but if this is a possibility, check the file you open afterwards
  94 +const detail::Combiner ExistingFile {1, false, false, {detail::_ExistingFile}};
  95 +const detail::Combiner ExistingDirectory {1, false, false, {detail::_ExistingDirectory}};
  96 +const detail::Combiner NonexistentPath {1, false, false, {detail::_NonexistentPath}};
  97 +
  98 +
  99 +}
... ...
include/CLI/Error.hpp 0 → 100644
  1 +#pragma once
  2 +
  3 +// Distributed under the LGPL version 3.0 license. See accompanying
  4 +// file LICENSE or https://github.com/henryiii/CLI11 for details.
  5 +
  6 +#include <string>
  7 +#include <exception>
  8 +#include <stdexcept>
  9 +
  10 +namespace CLI {
  11 +
  12 +// Error definitions
  13 +
  14 +
  15 +struct Error : public std::runtime_error {
  16 + int exit_code;
  17 + bool print_help;
  18 + Error(std::string parent, std::string name, int exit_code=255, bool print_help=true) : runtime_error(parent + ": " + name), exit_code(exit_code), print_help(print_help) {}
  19 +};
  20 +
  21 +struct Success : public Error {
  22 + Success() : Error("Success", "Successfully completed, should be caught and quit", 0, false) {}
  23 +};
  24 +
  25 +struct CallForHelp : public Error {
  26 + CallForHelp() : Error("CallForHelp", "This should be caught in your main function, see examples", 0) {}
  27 +};
  28 +
  29 +struct BadNameString : public Error {
  30 + BadNameString(std::string name) : Error("BadNameString", name, 1) {}
  31 +};
  32 +
  33 +
  34 +struct ParseError : public Error {
  35 + ParseError(std::string name) : Error("ParseError", name, 2) {}
  36 +};
  37 +
  38 +struct OptionAlreadyAdded : public Error {
  39 + OptionAlreadyAdded(std::string name) : Error("OptionAlreadyAdded", name, 3) {}
  40 +};
  41 +
  42 +struct OptionNotFound : public Error {
  43 + OptionNotFound(std::string name) : Error("OptionNotFound", name, 4) {}
  44 +};
  45 +
  46 +struct RequiredError : public Error {
  47 + RequiredError(std::string name) : Error("RequiredError", name, 5) {}
  48 +};
  49 +
  50 +struct PositionalError : public Error {
  51 + PositionalError(std::string name) : Error("PositionalError", name, 6) {}
  52 +};
  53 +
  54 +struct HorribleError : public Error {
  55 + HorribleError(std::string name) : Error("HorribleError", "(You should never see this error) " + name, 7) {}
  56 +};
  57 +struct IncorrectConstruction : public Error {
  58 + IncorrectConstruction(std::string name) : Error("IncorrectConstruction", name, 8) {}
  59 +};
  60 +struct EmptyError : public Error {
  61 + EmptyError(std::string name) : Error("EmptyError", name, 9) {}
  62 +};
  63 +
  64 +}
... ...
include/CLI/Option.hpp 0 → 100644
  1 +#pragma once
  2 +
  3 +// Distributed under the LGPL version 3.0 license. See accompanying
  4 +// file LICENSE or https://github.com/henryiii/CLI11 for details.
  5 +
  6 +#include <string>
  7 +#include <functional>
  8 +#include <vector>
  9 +#include <tuple>
  10 +#include <algorithm>
  11 +
  12 +#include "CLI/StringTools.hpp"
  13 +#include "CLI/Split.hpp"
  14 +#include "CLI/Combiner.hpp"
  15 +
  16 +namespace CLI {
  17 +
  18 +typedef std::vector<std::vector<std::string>> results_t;
  19 +typedef std::function<bool(results_t)> callback_t;
  20 +
  21 +
  22 +class App;
  23 +
  24 +class Option {
  25 + friend App;
  26 +protected:
  27 + // Config
  28 + std::vector<std::string> snames;
  29 + std::vector<std::string> lnames;
  30 + std::string pname;
  31 +
  32 + detail::Combiner opts;
  33 + std::string description;
  34 + callback_t callback;
  35 +
  36 + // These are for help strings
  37 + std::string defaultval;
  38 + std::string typeval;
  39 +
  40 + // Results
  41 + results_t results {};
  42 +
  43 +
  44 +public:
  45 + Option(std::string name, std::string description = "", detail::Combiner opts=Nothing, std::function<bool(results_t)> callback=[](results_t){return true;}) :
  46 + opts(opts), description(description), callback(callback){
  47 + std::tie(snames, lnames, pname) = detail::get_names(detail::split_names(name));
  48 + }
  49 +
  50 + /// Clear the parsed results (mostly for testing)
  51 + void clear() {
  52 + results.clear();
  53 + }
  54 +
  55 + /// True if option is required
  56 + bool required() const {
  57 + return opts.required;
  58 + }
  59 +
  60 + /// The number of arguments the option expects
  61 + int expected() const {
  62 + return opts.num;
  63 + }
  64 +
  65 + /// True if the argument can be given directly
  66 + bool positional() const {
  67 + return pname.length() > 0;
  68 + }
  69 +
  70 + /// True if option has at least one non-positional name
  71 + bool nonpositional() const {
  72 + return (snames.size() + lnames.size()) > 0;
  73 + }
  74 +
  75 + /// True if this should print the default string
  76 + bool defaulted() const {
  77 + return opts.defaulted;
  78 + }
  79 +
  80 + /// True if option has description
  81 + bool has_description() const {
  82 + return description.length() > 0;
  83 + }
  84 +
  85 + /// Get the description
  86 + const std::string& get_description() const {
  87 + return description;
  88 + }
  89 +
  90 + /// The name and any extras needed for positionals
  91 + std::string help_positional() const {
  92 + std::string out = pname;
  93 + if(expected()<1)
  94 + out = out + "x" + std::to_string(expected());
  95 + else if(expected()==-1)
  96 + out = out + "...";
  97 + out = required() ? out : "["+out+"]";
  98 + return out;
  99 + }
  100 +
  101 + // Just the pname
  102 + std::string get_pname() const {
  103 + return pname;
  104 + }
  105 +
  106 + /// Process the callback
  107 + bool run_callback() const {
  108 + if(opts.validators.size()>0) {
  109 + for(const std::string & result : flatten_results())
  110 + for(const std::function<bool(std::string)> &vali : opts.validators)
  111 + if(!vali(result))
  112 + return false;
  113 + }
  114 + return callback(results);
  115 + }
  116 +
  117 + /// If options share any of the same names, they are equal (not counting positional)
  118 + bool operator== (const Option& other) const {
  119 + for(const std::string &sname : snames)
  120 + for(const std::string &othersname : other.snames)
  121 + if(sname == othersname)
  122 + return true;
  123 + for(const std::string &lname : lnames)
  124 + for(const std::string &otherlname : other.lnames)
  125 + if(lname == otherlname)
  126 + return true;
  127 + return false;
  128 + }
  129 +
  130 + /// Gets a , sep list of names. Does not include the positional name.
  131 + std::string get_name() const {
  132 + std::vector<std::string> name_list;
  133 + for(const std::string& sname : snames)
  134 + name_list.push_back("-"+sname);
  135 + for(const std::string& lname : lnames)
  136 + name_list.push_back("--"+lname);
  137 + return detail::join(name_list);
  138 + }
  139 +
  140 + /// Check a name. Requires "-" or "--" for short / long, supports positional name
  141 + bool check_name(std::string name) const {
  142 +
  143 + if(name.length()>2 && name.substr(0,2) == "--")
  144 + return check_lname(name.substr(2));
  145 + else if (name.length()>1 && name.substr(0,1) == "-")
  146 + return check_sname(name.substr(1));
  147 + else
  148 + return name == pname;
  149 + }
  150 +
  151 + /// Requires "-" to be removed from string
  152 + bool check_sname(const std::string& name) const {
  153 + return std::find(std::begin(snames), std::end(snames), name) != std::end(snames);
  154 + }
  155 +
  156 + /// Requires "--" to be removed from string
  157 + bool check_lname(const std::string& name) const {
  158 + return std::find(std::begin(lnames), std::end(lnames), name) != std::end(lnames);
  159 + }
  160 +
  161 +
  162 + /// Puts a result at position r
  163 + void add_result(int r, std::string s) {
  164 + results.at(r).push_back(s);
  165 + }
  166 +
  167 + /// Starts a new results vector (used for r in add_result)
  168 + int get_new() {
  169 + results.emplace_back();
  170 + return results.size() - 1;
  171 + }
  172 +
  173 + /// Count the total number of times an option was passed
  174 + int count() const {
  175 + int out = 0;
  176 + for(const std::vector<std::string>& v : results)
  177 + out += v.size();
  178 + return out;
  179 + }
  180 +
  181 + /// Diagnostic representation
  182 + std::string string() const {
  183 + std::string val = "Option: " + get_name() + "\n"
  184 + + " " + description + "\n"
  185 + + " [";
  186 + for(const auto& item : results) {
  187 + if(&item!=&results[0])
  188 + val+="],[";
  189 + val += detail::join(item);
  190 + }
  191 + val += "]";
  192 + return val;
  193 + }
  194 +
  195 + /// The first half of the help print, name plus default, etc
  196 + std::string help_name() const {
  197 + std::stringstream out;
  198 + out << get_name();
  199 + if(expected() != 0) {
  200 + if(typeval != "")
  201 + out << " " << typeval;
  202 + if(defaultval != "")
  203 + out << "=" << defaultval;
  204 + if(expected() > 1)
  205 + out << " x " << expected();
  206 + if(expected() == -1)
  207 + out << " ...";
  208 + }
  209 + return out.str();
  210 + }
  211 +
  212 + /// Produce a flattened vector of results, vs. a vector of vectors.
  213 + std::vector<std::string> flatten_results() const {
  214 + std::vector<std::string> output;
  215 + for(const std::vector<std::string> result : results)
  216 + output.insert(std::end(output), std::begin(result), std::end(result));
  217 + return output;
  218 + }
  219 +
  220 +};
  221 +
  222 +
  223 +
  224 +}
... ...
include/CLI/Split.hpp 0 → 100644
  1 +#pragma once
  2 +
  3 +// Distributed under the LGPL version 3.0 license. See accompanying
  4 +// file LICENSE or https://github.com/henryiii/CLI11 for details.
  5 +
  6 +#include <string>
  7 +#include <vector>
  8 +#include <tuple>
  9 +
  10 +#include "CLI/Error.hpp"
  11 +#include "CLI/StringTools.hpp"
  12 +
  13 +namespace CLI {
  14 +namespace detail {
  15 +
  16 +// Returns false if not a short option. Otherwise, sets opt name and rest and returns true
  17 +inline bool split_short(const std::string &current, std::string &name, std::string &rest) {
  18 + if(current.size()>1 && current[0] == '-' && valid_first_char(current[1])) {
  19 + name = current.substr(1,1);
  20 + rest = current.substr(2);
  21 + return true;
  22 + } else
  23 + return false;
  24 +}
  25 +
  26 +// Returns false if not a long option. Otherwise, sets opt name and other side of = and returns true
  27 +inline bool split_long(const std::string &current, std::string &name, std::string &value) {
  28 + if(current.size()>2 && current.substr(0,2) == "--" && valid_first_char(current[2])) {
  29 + auto loc = current.find("=");
  30 + if(loc != std::string::npos) {
  31 + name = current.substr(2,loc-2);
  32 + value = current.substr(loc+1);
  33 + } else {
  34 + name = current.substr(2);
  35 + value = "";
  36 + }
  37 + return true;
  38 + } else
  39 + return false;
  40 +}
  41 +
  42 +// Splits a string into multiple long and short names
  43 +inline std::vector<std::string> split_names(std::string current) {
  44 + std::vector<std::string> output;
  45 + size_t val;
  46 + while((val = current.find(",")) != std::string::npos) {
  47 + output.push_back(current.substr(0,val));
  48 + current = current.substr(val+1);
  49 + }
  50 + output.push_back(current);
  51 + return output;
  52 +
  53 +}
  54 +
  55 +/// Get a vector of short names, one of long names, and a single name
  56 +inline std::tuple<std::vector<std::string>,std::vector<std::string>, std::string>
  57 + get_names(const std::vector<std::string> &input) {
  58 +
  59 + std::vector<std::string> short_names;
  60 + std::vector<std::string> long_names;
  61 + std::string pos_name;
  62 +
  63 + for(std::string name : input) {
  64 + if(name.length() == 0)
  65 + continue;
  66 + else if(name.length() > 1 && name[0] == '-' && name[1] != '-') {
  67 + if(name.length()==2 && valid_first_char(name[1]))
  68 + short_names.push_back(std::string(1,name[1]));
  69 + else
  70 + throw BadNameString("Invalid one char name: "+name);
  71 + } else if(name.length() > 2 && name.substr(0,2) == "--") {
  72 + name = name.substr(2);
  73 + if(valid_name_string(name))
  74 + long_names.push_back(name);
  75 + else
  76 + throw BadNameString("Bad long name: "+name);
  77 + } else if(name == "-" || name == "--") {
  78 + throw BadNameString("Must have a name, not just dashes");
  79 + } else {
  80 + if(pos_name.length() > 0)
  81 + throw BadNameString("Only one positional name allowed, remove: "+name);
  82 + pos_name = name;
  83 +
  84 + }
  85 + }
  86 +
  87 + return std::tuple<std::vector<std::string>,std::vector<std::string>, std::string>
  88 + (short_names, long_names, pos_name);
  89 +}
  90 +
  91 +
  92 +}
  93 +}
... ...
include/CLI/StringTools.hpp 0 → 100644
  1 +#pragma once
  2 +
  3 +// Distributed under the LGPL version 3.0 license. See accompanying
  4 +// file LICENSE or https://github.com/henryiii/CLI11 for details.
  5 +
  6 +#include <string>
  7 +#include <sstream>
  8 +#include <iomanip>
  9 +#include <locale>
  10 +#include <type_traits>
  11 +
  12 +namespace CLI {
  13 +namespace detail {
  14 +
  15 +
  16 +/// Simple function to join a string
  17 +template <typename T>
  18 +std::string join(const T& v, std::string delim = ",") {
  19 + std::ostringstream s;
  20 + size_t start = 0;
  21 + for (const auto& i : v) {
  22 + if(start++ > 0)
  23 + s << delim;
  24 + s << i;
  25 + }
  26 + return s.str();
  27 +}
  28 +
  29 +/// Print a two part "help" string
  30 +void format_help(std::stringstream &out, std::string name, std::string description, size_t wid) {
  31 + name = " " + name;
  32 + out << std::setw(wid) << std::left << name;
  33 + if(description != "") {
  34 + if(name.length()>=wid)
  35 + out << std::endl << std::setw(wid) << "";
  36 + out << description << std::endl;
  37 + }
  38 +}
  39 +
  40 +/// Verify the first character of an option
  41 +template<typename T>
  42 +bool valid_first_char(T c) {
  43 + return std::isalpha(c) || c=='_';
  44 +}
  45 +
  46 +/// Verify following characters of an option
  47 +template<typename T>
  48 +bool valid_later_char(T c) {
  49 + return std::isalnum(c) || c=='_' || c=='.' || c=='-';
  50 +}
  51 +
  52 +/// Verify an option name
  53 +inline bool valid_name_string(const std::string &str) {
  54 + if(str.size()<1 || !valid_first_char(str[0]))
  55 + return false;
  56 + for(auto c : str.substr(1))
  57 + if(!valid_later_char(c))
  58 + return false;
  59 + return true;
  60 +}
  61 +
  62 +
  63 +
  64 +}
  65 +}
... ...
include/CLI/TypeTools.hpp 0 → 100644
  1 +#pragma once
  2 +
  3 +// Distributed under the LGPL version 3.0 license. See accompanying
  4 +// file LICENSE or https://github.com/henryiii/CLI11 for details.
  5 +
  6 +#include <vector>
  7 +#include <type_traits>
  8 +#include <string>
  9 +#include <exception>
  10 +
  11 +namespace CLI {
  12 +
  13 +// Type tools
  14 +
  15 +// Copied from C++14
  16 +#if __cplusplus < 201402L
  17 +template< bool B, class T = void >
  18 +using enable_if_t = typename std::enable_if<B,T>::type;
  19 +#else
  20 +// If your compiler supports C++14, you can use that definition instead
  21 +using std::enable_if_t;
  22 +#endif
  23 +
  24 +template <typename T>
  25 +struct is_vector {
  26 + static const bool value = false;
  27 +};
  28 +
  29 +
  30 +template<class T, class A>
  31 +struct is_vector<std::vector<T, A> > {
  32 + static bool const value = true;
  33 +};
  34 +
  35 +template <typename T>
  36 +struct is_bool {
  37 + static const bool value = false;
  38 +};
  39 +
  40 +template<>
  41 +struct is_bool<bool> {
  42 + static bool const value = true;
  43 +};
  44 +
  45 +
  46 +namespace detail {
  47 + // Based generally on https://rmf.io/cxx11/almost-static-if
  48 + /// Simple empty scoped class
  49 + enum class enabler {};
  50 +
  51 + /// An instance to use in EnableIf
  52 + constexpr enabler dummy = {};
  53 +
  54 +
  55 + // Type name print
  56 +
  57 + /// Was going to be based on
  58 + /// http://stackoverflow.com/questions/1055452/c-get-name-of-type-in-template
  59 + /// But this is cleaner and works better in this case
  60 +
  61 + template<typename T,
  62 + enable_if_t<std::is_integral<T>::value && std::is_signed<T>::value, detail::enabler> = detail::dummy>
  63 + constexpr const char* type_name() {
  64 + return "INT";
  65 + }
  66 +
  67 + template<typename T,
  68 + enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value, detail::enabler> = detail::dummy>
  69 + constexpr const char* type_name() {
  70 + return "UINT";
  71 + }
  72 +
  73 +
  74 + template<typename T,
  75 + enable_if_t<std::is_floating_point<T>::value, detail::enabler> = detail::dummy>
  76 + constexpr const char* type_name() {
  77 + return "FLOAT";
  78 + }
  79 +
  80 +
  81 + /// This one should not be used, since vector types print the internal type
  82 + template<typename T,
  83 + enable_if_t<is_vector<T>::value, detail::enabler> = detail::dummy>
  84 + constexpr const char* type_name() {
  85 + return "VECTOR";
  86 + }
  87 +
  88 +
  89 + template<typename T,
  90 + enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value && !is_vector<T>::value
  91 + , detail::enabler> = detail::dummy>
  92 + constexpr const char* type_name() {
  93 + return "STRING";
  94 + }
  95 +
  96 +
  97 +
  98 + // Lexical cast
  99 +
  100 +
  101 + /// Integers
  102 + template<typename T, enable_if_t<std::is_integral<T>::value, detail::enabler> = detail::dummy>
  103 + bool lexical_cast(std::string input, T& output) {
  104 + try{
  105 + output = (T) std::stoll(input);
  106 + return true;
  107 + } catch (std::invalid_argument) {
  108 + return false;
  109 + } catch (std::out_of_range) {
  110 + return false;
  111 + }
  112 + }
  113 +
  114 + /// Floats
  115 + template<typename T, enable_if_t<std::is_floating_point<T>::value, detail::enabler> = detail::dummy>
  116 + bool lexical_cast(std::string input, T& output) {
  117 + try{
  118 + output = (T) std::stold(input);
  119 + return true;
  120 + } catch (std::invalid_argument) {
  121 + return false;
  122 + } catch (std::out_of_range) {
  123 + return false;
  124 + }
  125 + }
  126 +
  127 + /// Vector
  128 + template<typename T,
  129 + enable_if_t<is_vector<T>::value, detail::enabler> = detail::dummy>
  130 + bool lexical_cast(std::string input, T& output) {
  131 + if(output.size() == input.size())
  132 + output.resize(input.size());
  133 + for(size_t i=0; i<input.size(); i++)
  134 + output[i] = input[i];
  135 + return true;
  136 + }
  137 +
  138 + /// String and similar
  139 + template<typename T,
  140 + enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value && !is_vector<T>::value
  141 + , detail::enabler> = detail::dummy>
  142 + bool lexical_cast(std::string input, T& output) {
  143 + output = input;
  144 + return true;
  145 + }
  146 +
  147 +
  148 +}
  149 +}
... ...
include/CLI/Value.hpp 0 → 100644
  1 +#pragma once
  2 +
  3 +// Distributed under the LGPL version 3.0 license. See accompanying
  4 +// file LICENSE or https://github.com/henryiii/CLI11 for details.
  5 +
  6 +#include <string>
  7 +#include <memory>
  8 +
  9 +#include "CLI/Error.hpp"
  10 +
  11 +namespace CLI {
  12 +
  13 +class App;
  14 +
  15 +// Prototype return value test
  16 +template <typename T>
  17 +class Value {
  18 + friend App;
  19 +protected:
  20 + std::shared_ptr<std::unique_ptr<T>> value {new std::unique_ptr<T>()};
  21 + std::string name;
  22 +public:
  23 + Value(std::string name) : name(name) {}
  24 +
  25 + operator bool() const {return (bool) *value;}
  26 +
  27 + T& get() const {
  28 + if(*value)
  29 + return **value;
  30 + else
  31 + throw EmptyError(name);
  32 + }
  33 + /// Note this does not throw on assignment, though
  34 + /// afterwards it seems to work fine. Best to use
  35 + /// explicit * notation.
  36 + T& operator *() const {
  37 + return get();
  38 + }
  39 +};
  40 +
  41 +}
... ...
scripts/MakeSingleHeader.py 0 → 100755
  1 +#!/usr/bin/env python
  2 +
  3 +# Requires Python 3.6
  4 +
  5 +from plumbum import local, cli, FG
  6 +import re
  7 +
  8 +includes_local = re.compile(r"""^#include "(.*)"$""", re.MULTILINE)
  9 +includes_system = re.compile(r"""^#include \<(.*)\>$""", re.MULTILINE)
  10 +
  11 +DIR = local.path(__file__).dirname
  12 +BDIR = DIR / '../include'
  13 +
  14 +class MakeHeader(cli.Application):
  15 +
  16 + def main(self, out : cli.NonexistentPath = BDIR / 'CLI11.hpp'):
  17 + main_header = BDIR / 'CLI/CLI.hpp'
  18 + header = main_header.read()
  19 +
  20 + include_files = includes_local.findall(header)
  21 +
  22 + headers = set()
  23 + output = ''
  24 + with open('output.hpp', 'w') as f:
  25 + for inc in include_files:
  26 + inner = (BDIR / inc).read()
  27 + headers |= set(includes_system.findall(inner))
  28 + output += f'\n// From {inc}\n\n'
  29 + output += inner[inner.find('namespace'):]
  30 +
  31 + header_list = '\n'.join(f'#include <{h}>' for h in headers)
  32 +
  33 + output = f'''\
  34 +#pragma once
  35 +
  36 +// Distributed under the LGPL version 3.0 license. See accompanying
  37 +// file LICENSE or https://github.com/henryiii/CLI11 for details.
  38 +
  39 +// This file was generated using MakeSingleHeader.py in CLI11/scripts
  40 +// This has the complete CLI library in one file.
  41 +
  42 +{header_list}
  43 +{output}'''
  44 +
  45 + with out.open('w') as f:
  46 + f.write(output)
  47 +
  48 + print(f"Created {out}")
  49 +
  50 +if __name__ == '__main__':
  51 + MakeHeader()
  52 +
  53 +
... ...
tests/CLITest.cpp
1 1  
2   -#include "CLI.hpp"
  2 +#include "CLI/CLI.hpp"
3 3 #include "gtest/gtest.h"
4 4 #include <fstream>
5 5  
... ...
tests/SmallTest.cpp
1   -#include "CLI.hpp"
  1 +#include "CLI/CLI.hpp"
2 2 #include "gtest/gtest.h"
3 3 #include <cstdio>
4 4 #include <fstream>
... ...