clipp.h
Go to the documentation of this file.
1 /*****************************************************************************
2  *
3  * CLIPP - command line interfaces for modern C++
4  *
5  * released under MIT license
6  *
7  * (c) 2017 André Müller; [email protected]
8  *
9  *****************************************************************************/
10 
11 #ifndef AM_CLIPP_H__
12 #define AM_CLIPP_H__
13 
14 #include <cstring>
15 #include <string>
16 #include <cstdlib>
17 #include <cstring>
18 #include <cctype>
19 #include <cmath>
20 #include <memory>
21 #include <vector>
22 #include <limits>
23 #include <stack>
24 #include <algorithm>
25 #include <sstream>
26 #include <utility>
27 #include <iterator>
28 #include <functional>
29 
30 
31 /*************************************************************************/
36 namespace clipp {
37 
38 
39 
40 /*****************************************************************************
41  *
42  * basic constants and datatype definitions
43  *
44  *****************************************************************************/
45 using arg_index = int;
46 
47 using arg_string = std::string;
48 using doc_string = std::string;
49 
50 using arg_list = std::vector<arg_string>;
51 
52 
53 
54 /*************************************************************************/
59 enum class tri : char { no, yes, either };
60 
61 inline constexpr bool operator == (tri t, bool b) noexcept {
62  return b ? t != tri::no : t != tri::yes;
63 }
64 inline constexpr bool operator == (bool b, tri t) noexcept { return (t == b); }
65 inline constexpr bool operator != (tri t, bool b) noexcept { return !(t == b); }
66 inline constexpr bool operator != (bool b, tri t) noexcept { return !(t == b); }
67 
68 
69 
70 /*************************************************************************/
75 class subrange {
76 public:
77  using size_type = arg_string::size_type;
78 
80  explicit constexpr
81  subrange() noexcept :
82  at_{arg_string::npos}, length_{0}
83  {}
84 
86  explicit constexpr
87  subrange(size_type pos, size_type len) noexcept :
88  at_{pos}, length_{len}
89  {}
90 
92  constexpr size_type at() const noexcept { return at_; }
94  constexpr size_type length() const noexcept { return length_; }
95 
97  constexpr bool prefix() const noexcept {
98  return at_ == 0 && length_ > 0;
99  }
100 
102  constexpr explicit operator bool () const noexcept {
103  return at_ != arg_string::npos && length_ > 0;
104  }
105 
106 private:
107  size_type at_;
108  size_type length_;
109 };
110 
111 
112 
113 /*************************************************************************/
118 using match_predicate = std::function<bool(const arg_string&)>;
119 using match_function = std::function<subrange(const arg_string&)>;
120 
121 
122 
123 
124 
125 
126 /*************************************************************************/
132 namespace traits {
133 
134 /*************************************************************************/
139 template<class Fn, class Ret, class... Args>
140 constexpr auto
141 check_is_callable(int) -> decltype(
142  std::declval<Fn>()(std::declval<Args>()...),
143  std::integral_constant<bool,
144  std::is_same<Ret,typename std::result_of<Fn(Args...)>::type>::value>{} );
145 
146 template<class,class,class...>
147 constexpr auto
148 check_is_callable(long) -> std::false_type;
149 
150 template<class Fn, class Ret>
151 constexpr auto
152 check_is_callable_without_arg(int) -> decltype(
153  std::declval<Fn>()(),
154  std::integral_constant<bool,
155  std::is_same<Ret,typename std::result_of<Fn()>::type>::value>{} );
156 
157 template<class,class>
158 constexpr auto
159 check_is_callable_without_arg(long) -> std::false_type;
160 
161 
162 
163 template<class Fn, class... Args>
164 constexpr auto
165 check_is_void_callable(int) -> decltype(
166  std::declval<Fn>()(std::declval<Args>()...), std::true_type{});
167 
168 template<class,class,class...>
169 constexpr auto
170 check_is_void_callable(long) -> std::false_type;
171 
172 template<class Fn>
173 constexpr auto
174 check_is_void_callable_without_arg(int) -> decltype(
175  std::declval<Fn>()(), std::true_type{});
176 
177 template<class>
178 constexpr auto
179 check_is_void_callable_without_arg(long) -> std::false_type;
180 
181 
182 
183 template<class Fn, class Ret>
184 struct is_callable;
185 
186 
187 template<class Fn, class Ret, class... Args>
188 struct is_callable<Fn, Ret(Args...)> :
189  decltype(check_is_callable<Fn,Ret,Args...>(0))
190 {};
191 
192 template<class Fn, class Ret>
193 struct is_callable<Fn,Ret()> :
194  decltype(check_is_callable_without_arg<Fn,Ret>(0))
195 {};
196 
197 
198 template<class Fn, class... Args>
199 struct is_callable<Fn, void(Args...)> :
200  decltype(check_is_void_callable<Fn,Args...>(0))
201 {};
202 
203 template<class Fn>
204 struct is_callable<Fn,void()> :
205  decltype(check_is_void_callable_without_arg<Fn>(0))
206 {};
207 
208 
209 
210 /*************************************************************************/
215 template<class T>
216 constexpr auto
217 check_is_input_range(int) -> decltype(
218  begin(std::declval<T>()), end(std::declval<T>()),
219  std::true_type{});
220 
221 template<class T>
222 constexpr auto
223 check_is_input_range(char) -> decltype(
224  std::begin(std::declval<T>()), std::end(std::declval<T>()),
225  std::true_type{});
226 
227 template<class>
228 constexpr auto
229 check_is_input_range(long) -> std::false_type;
230 
231 template<class T>
232 struct is_input_range :
233  decltype(check_is_input_range<T>(0))
234 {};
235 
236 
237 
238 /*************************************************************************/
243 template<class T>
244 constexpr auto
245 check_has_size_getter(int) ->
246  decltype(std::declval<T>().size(), std::true_type{});
247 
248 template<class>
249 constexpr auto
250 check_has_size_getter(long) -> std::false_type;
251 
252 template<class T>
254  decltype(check_has_size_getter<T>(0))
255 {};
256 
257 } // namespace traits
258 
259 
260 
261 
262 
263 
264 /*************************************************************************/
270 namespace detail {
271 
272 
273 /*************************************************************************/
278 inline bool
279 fwd_to_unsigned_int(const char*& s)
280 {
281  if(!s) return false;
282  for(; std::isspace(*s); ++s);
283  if(!s[0] || s[0] == '-') return false;
284  if(s[0] == '-') return false;
285  return true;
286 }
287 
288 
289 /*************************************************************************/
294 template<class T, class V, bool = (sizeof(V) > sizeof(T))>
295 struct limits_clamped {
296  static T from(const V& v) {
297  if(v > V(std::numeric_limits<T>::max())) {
298  return std::numeric_limits<T>::max();
299  }
300  if(v < V(std::numeric_limits<T>::lowest())) {
301  return std::numeric_limits<T>::lowest();
302  }
303  return T(v);
304  }
305 };
306 
307 template<class T, class V>
308 struct limits_clamped<T,V,false> {
309  static T from(const V& v) { return T(v); }
310 };
311 
312 
313 /*************************************************************************/
318 template<class T, class V>
319 inline T clamped_on_limits(const V& v) {
320  return limits_clamped<T,V>::from(v);
321 }
322 
323 
324 
325 
326 /*************************************************************************/
331 template<class T>
332 struct make;
333 
334 template<>
335 struct make<bool> {
336  static inline bool from(const char* s) {
337  if(!s) return false;
338  return static_cast<bool>(s);
339  }
340 };
341 
342 template<>
343 struct make<unsigned char> {
344  static inline unsigned char from(const char* s) {
345  if(!fwd_to_unsigned_int(s)) return (0);
346  return clamped_on_limits<unsigned char>(std::strtoull(s,nullptr,10));
347  }
348 };
349 
350 template<>
351 struct make<unsigned short int> {
352  static inline unsigned short int from(const char* s) {
353  if(!fwd_to_unsigned_int(s)) return (0);
354  return clamped_on_limits<unsigned short int>(std::strtoull(s,nullptr,10));
355  }
356 };
357 
358 template<>
359 struct make<unsigned int> {
360  static inline unsigned int from(const char* s) {
361  if(!fwd_to_unsigned_int(s)) return (0);
362  return clamped_on_limits<unsigned int>(std::strtoull(s,nullptr,10));
363  }
364 };
365 
366 template<>
367 struct make<unsigned long int> {
368  static inline unsigned long int from(const char* s) {
369  if(!fwd_to_unsigned_int(s)) return (0);
370  return clamped_on_limits<unsigned long int>(std::strtoull(s,nullptr,10));
371  }
372 };
373 
374 template<>
375 struct make<unsigned long long int> {
376  static inline unsigned long long int from(const char* s) {
377  if(!fwd_to_unsigned_int(s)) return (0);
378  return clamped_on_limits<unsigned long long int>(std::strtoull(s,nullptr,10));
379  }
380 };
381 
382 template<>
383 struct make<char> {
384  static inline char from(const char* s) {
385  //parse as single character?
386  const auto n = std::strlen(s);
387  if(n == 1) return s[0];
388  //parse as integer
389  return clamped_on_limits<char>(std::strtoll(s,nullptr,10));
390  }
391 };
392 
393 template<>
394 struct make<short int> {
395  static inline short int from(const char* s) {
396  return clamped_on_limits<short int>(std::strtoll(s,nullptr,10));
397  }
398 };
399 
400 template<>
401 struct make<int> {
402  static inline int from(const char* s) {
403  return clamped_on_limits<int>(std::strtoll(s,nullptr,10));
404  }
405 };
406 
407 template<>
408 struct make<long int> {
409  static inline long int from(const char* s) {
410  return clamped_on_limits<long int>(std::strtoll(s,nullptr,10));
411  }
412 };
413 
414 template<>
415 struct make<long long int> {
416  static inline long long int from(const char* s) {
417  return (std::strtoll(s,nullptr,10));
418  }
419 };
420 
421 template<>
422 struct make<float> {
423  static inline float from(const char* s) {
424  return (std::strtof(s,nullptr));
425  }
426 };
427 
428 template<>
429 struct make<double> {
430  static inline double from(const char* s) {
431  return (std::strtod(s,nullptr));
432  }
433 };
434 
435 template<>
436 struct make<long double> {
437  static inline long double from(const char* s) {
438  return (std::strtold(s,nullptr));
439  }
440 };
441 
442 template<>
443 struct make<std::string> {
444  static inline std::string from(const char* s) {
445  return std::string(s);
446  }
447 };
448 
449 
450 
451 /*************************************************************************/
456 template<class T, class V = T>
457 class assign_value
458 {
459 public:
460  template<class X>
461  explicit constexpr
462  assign_value(T& target, X&& value) noexcept :
463  t_{std::addressof(target)}, v_{std::forward<X>(value)}
464  {}
465 
466  void operator () () const {
467  if(t_) *t_ = v_;
468  }
469 
470 private:
471  T* t_;
472  V v_;
473 };
474 
475 
476 
477 /*************************************************************************/
482 class flip_bool
483 {
484 public:
485  explicit constexpr
486  flip_bool(bool& target) noexcept :
487  b_{&target}
488  {}
489 
490  void operator () () const {
491  if(b_) *b_ = !*b_;
492  }
493 
494 private:
495  bool* b_;
496 };
497 
498 
499 
500 /*************************************************************************/
505 template<class T>
506 class increment
507 {
508 public:
509  explicit constexpr
510  increment(T& target) noexcept : t_{std::addressof(target)} {}
511 
512  void operator () () const {
513  if(t_) ++(*t_);
514  }
515 
516 private:
517  T* t_;
518 };
519 
520 
521 
522 /*************************************************************************/
527 template<class T>
528 class decrement
529 {
530 public:
531  explicit constexpr
532  decrement(T& target) noexcept : t_{std::addressof(target)} {}
533 
534  void operator () () const {
535  if(t_) --(*t_);
536  }
537 
538 private:
539  T* t_;
540 };
541 
542 
543 
544 /*************************************************************************/
549 template<class T>
550 class increment_by
551 {
552 public:
553  explicit constexpr
554  increment_by(T& target, T by) noexcept :
555  t_{std::addressof(target)}, by_{std::move(by)}
556  {}
557 
558  void operator () () const {
559  if(t_) (*t_) += by_;
560  }
561 
562 private:
563  T* t_;
564  T by_;
565 };
566 
567 
568 
569 
570 /*************************************************************************/
575 template<class T>
576 class map_arg_to
577 {
578 public:
579  explicit constexpr
580  map_arg_to(T& target) noexcept : t_{std::addressof(target)} {}
581 
582  void operator () (const char* s) const {
583  if(t_ && s && (std::strlen(s) > 0))
585  }
586 
587 private:
588  T* t_;
589 };
590 
591 
592 //-------------------------------------------------------------------
596 template<class T>
597 class map_arg_to<std::vector<T>>
598 {
599 public:
600  map_arg_to(std::vector<T>& target): t_{std::addressof(target)} {}
601 
602  void operator () (const char* s) const {
603  if(t_ && s) t_->push_back(detail::make<T>::from(s));
604  }
605 
606 private:
607  std::vector<T>* t_;
608 };
609 
610 
611 //-------------------------------------------------------------------
616 template<>
617 class map_arg_to<bool>
618 {
619 public:
620  map_arg_to(bool& target): t_{&target} {}
621 
622  void operator () (const char* s) const {
623  if(t_ && s) *t_ = true;
624  }
625 
626 private:
627  bool* t_;
628 };
629 
630 
631 } // namespace detail
632 
633 
634 
635 
636 
637 
638 /*************************************************************************/
644 namespace str {
645 
646 
647 /*************************************************************************/
652 template<class T>
653 T make(const arg_string& s)
654 {
655  return detail::make<T>::from(s);
656 }
657 
658 
659 
660 /*************************************************************************/
665 template<class C, class T, class A>
666 inline void
667 trimr(std::basic_string<C,T,A>& s)
668 {
669  if(s.empty()) return;
670 
671  s.erase(
672  std::find_if_not(s.rbegin(), s.rend(),
673  [](char c) { return std::isspace(c);} ).base(),
674  s.end() );
675 }
676 
677 
678 /*************************************************************************/
683 template<class C, class T, class A>
684 inline void
685 triml(std::basic_string<C,T,A>& s)
686 {
687  if(s.empty()) return;
688 
689  s.erase(
690  s.begin(),
691  std::find_if_not(s.begin(), s.end(),
692  [](char c) { return std::isspace(c);})
693  );
694 }
695 
696 
697 /*************************************************************************/
702 template<class C, class T, class A>
703 inline void
704 trim(std::basic_string<C,T,A>& s)
705 {
706  triml(s);
707  trimr(s);
708 }
709 
710 
711 /*************************************************************************/
716 template<class C, class T, class A>
717 inline void
718 remove_ws(std::basic_string<C,T,A>& s)
719 {
720  if(s.empty()) return;
721 
722  s.erase(std::remove_if(s.begin(), s.end(),
723  [](char c) { return std::isspace(c); }),
724  s.end() );
725 }
726 
727 
728 /*************************************************************************/
734 template<class C, class T, class A>
735 inline bool
736 has_prefix(const std::basic_string<C,T,A>& subject,
737  const std::basic_string<C,T,A>& prefix)
738 {
739  if(prefix.size() > subject.size()) return false;
740  return subject.find(prefix) == 0;
741 }
742 
743 
744 /*************************************************************************/
750 template<class C, class T, class A>
751 inline bool
752 has_postfix(const std::basic_string<C,T,A>& subject,
753  const std::basic_string<C,T,A>& postfix)
754 {
755  if(postfix.size() > subject.size()) return false;
756  return (subject.size() - postfix.size()) == subject.find(postfix);
757 }
758 
759 
760 
761 /*************************************************************************/
770 template<class InputRange>
771 auto
772 longest_common_prefix(const InputRange& strs)
773  -> typename std::decay<decltype(*begin(strs))>::type
774 {
775  static_assert(traits::is_input_range<InputRange>(),
776  "parameter must satisfy the InputRange concept");
777 
778  static_assert(traits::has_size_getter<
779  typename std::decay<decltype(*begin(strs))>::type>(),
780  "elements of input range must have a ::size() member function");
781 
782  using std::begin;
783  using std::end;
784 
785  using item_t = typename std::decay<decltype(*begin(strs))>::type;
786  using str_size_t = typename std::decay<decltype(begin(strs)->size())>::type;
787 
788  const auto n = size_t(distance(begin(strs), end(strs)));
789  if(n < 1) return item_t("");
790  if(n == 1) return *begin(strs);
791 
792  //length of shortest string
793  auto m = std::min_element(begin(strs), end(strs),
794  [](const item_t& a, const item_t& b) {
795  return a.size() < b.size(); })->size();
796 
797  //check each character until we find a mismatch
798  for(str_size_t i = 0; i < m; ++i) {
799  for(str_size_t j = 1; j < n; ++j) {
800  if(strs[j][i] != strs[j-1][i])
801  return strs[0].substr(0, i);
802  }
803  }
804  return strs[0].substr(0, m);
805 }
806 
807 
808 
809 /*************************************************************************/
817 template<class C, class T, class A, class InputRange>
818 subrange
819 longest_substring_match(const std::basic_string<C,T,A>& arg,
820  const InputRange& substrings)
821 {
822  using string_t = std::basic_string<C,T,A>;
823 
824  static_assert(traits::is_input_range<InputRange>(),
825  "parameter must satisfy the InputRange concept");
826 
827  static_assert(std::is_same<string_t,
828  typename std::decay<decltype(*begin(substrings))>::type>(),
829  "substrings must have same type as 'arg'");
830 
831  auto i = string_t::npos;
832  auto n = string_t::size_type(0);
833  for(const auto& s : substrings) {
834  auto j = arg.find(s);
835  if(j != string_t::npos && s.size() > n) {
836  i = j;
837  n = s.size();
838  }
839  }
840  return subrange{i,n};
841 }
842 
843 
844 
845 /*************************************************************************/
853 template<class C, class T, class A, class InputRange>
854 subrange
855 longest_prefix_match(const std::basic_string<C,T,A>& arg,
856  const InputRange& prefixes)
857 {
858  using string_t = std::basic_string<C,T,A>;
859  using s_size_t = typename string_t::size_type;
860 
861  static_assert(traits::is_input_range<InputRange>(),
862  "parameter must satisfy the InputRange concept");
863 
864  static_assert(std::is_same<string_t,
865  typename std::decay<decltype(*begin(prefixes))>::type>(),
866  "prefixes must have same type as 'arg'");
867 
868  auto i = string_t::npos;
869  auto n = s_size_t(0);
870  for(const auto& s : prefixes) {
871  auto j = arg.find(s);
872  if(j == 0 && s.size() > n) {
873  i = 0;
874  n = s.size();
875  }
876  }
877  return subrange{i,n};
878 }
879 
880 
881 
882 /*************************************************************************/
887 template<class C, class T, class A>
888 inline subrange
889 substring_match(const std::basic_string<C,T,A>& subject,
890  const std::basic_string<C,T,A>& query)
891 {
892  if(subject.empty() || query.empty()) return subrange{};
893  auto i = subject.find(query);
894  if(i == std::basic_string<C,T,A>::npos) return subrange{};
895  return subrange{i,query.size()};
896 }
897 
898 
899 
900 /*************************************************************************/
907 template<class C, class T, class A>
908 subrange
909 first_number_match(std::basic_string<C,T,A> s,
910  C digitSeparator = C(','),
911  C decimalPoint = C('.'),
912  C exponential = C('e'))
913 {
914  using string_t = std::basic_string<C,T,A>;
915 
916  str::trim(s);
917  if(s.empty()) return subrange{};
918 
919  auto i = s.find_first_of("0123456789+-");
920  if(i == string_t::npos) {
921  i = s.find(decimalPoint);
922  if(i == string_t::npos) return subrange{};
923  }
924 
925  bool point = false;
926  bool sep = false;
927  auto exp = string_t::npos;
928  auto j = i + 1;
929  for(; j < s.size(); ++j) {
930  if(s[j] == digitSeparator) {
931  if(!sep) sep = true; else break;
932  }
933  else {
934  sep = false;
935  if(s[j] == decimalPoint) {
936  //only one decimal point before exponent allowed
937  if(!point && exp == string_t::npos) point = true; else break;
938  }
939  else if(std::tolower(s[j]) == std::tolower(exponential)) {
940  //only one exponent separator allowed
941  if(exp == string_t::npos) exp = j; else break;
942  }
943  else if(exp != string_t::npos && (exp+1) == j) {
944  //only sign or digit after exponent separator
945  if(s[j] != '+' && s[j] != '-' && !std::isdigit(s[j])) break;
946  }
947  else if(!std::isdigit(s[j])) {
948  break;
949  }
950  }
951  }
952 
953  //if length == 1 then must be a digit
954  if(j-i == 1 && !std::isdigit(s[i])) return subrange{};
955 
956  return subrange{i,j-i};
957 }
958 
959 
960 
961 /*************************************************************************/
967 template<class C, class T, class A>
968 subrange
969 first_integer_match(std::basic_string<C,T,A> s,
970  C digitSeparator = C(','))
971 {
972  using string_t = std::basic_string<C,T,A>;
973 
974  str::trim(s);
975  if(s.empty()) return subrange{};
976 
977  auto i = s.find_first_of("0123456789+-");
978  if(i == string_t::npos) return subrange{};
979 
980  bool sep = false;
981  auto j = i + 1;
982  for(; j < s.size(); ++j) {
983  if(s[j] == digitSeparator) {
984  if(!sep) sep = true; else break;
985  }
986  else {
987  sep = false;
988  if(!std::isdigit(s[j])) break;
989  }
990  }
991 
992  //if length == 1 then must be a digit
993  if(j-i == 1 && !std::isdigit(s[i])) return subrange{};
994 
995  return subrange{i,j-i};
996 }
997 
998 
999 
1000 /*************************************************************************/
1005 template<class C, class T, class A>
1006 bool represents_number(const std::basic_string<C,T,A>& candidate,
1007  C digitSeparator = C(','),
1008  C decimalPoint = C('.'),
1009  C exponential = C('e'))
1010 {
1011  const auto match = str::first_number_match(candidate, digitSeparator,
1012  decimalPoint, exponential);
1013 
1014  return (match && match.length() == candidate.size());
1015 }
1016 
1017 
1018 
1019 /*************************************************************************/
1024 template<class C, class T, class A>
1025 bool represents_integer(const std::basic_string<C,T,A>& candidate,
1026  C digitSeparator = C(','))
1027 {
1028  const auto match = str::first_integer_match(candidate, digitSeparator);
1029  return (match && match.length() == candidate.size());
1030 }
1031 
1032 } // namespace str
1034 
1035 
1036 
1037 
1038 
1039 /*************************************************************************/
1045 template<class T, class V>
1047 set(T& target, V value) {
1048  return detail::assign_value<T>{target, std::move(value)};
1049 }
1050 
1051 
1052 
1053 /*************************************************************************/
1062 template<class T>
1063 inline detail::map_arg_to<T>
1064 set(T& target) {
1065  return detail::map_arg_to<T>{target};
1066 }
1067 
1068 
1069 
1070 /*************************************************************************/
1075 inline detail::assign_value<bool>
1076 set(bool& target) {
1077  return detail::assign_value<bool>{target,true};
1078 }
1079 
1080 /*************************************************************************/
1085 inline detail::assign_value<bool>
1086 unset(bool& target) {
1087  return detail::assign_value<bool>{target,false};
1088 }
1089 
1090 /*************************************************************************/
1095 inline detail::flip_bool
1096 flip(bool& b) {
1097  return detail::flip_bool(b);
1098 }
1099 
1100 
1101 
1102 
1103 
1104 /*************************************************************************/
1109 template<class T>
1110 inline detail::increment<T>
1111 increment(T& target) {
1112  return detail::increment<T>{target};
1113 }
1114 
1115 /*************************************************************************/
1120 template<class T>
1121 inline detail::increment_by<T>
1122 increment(T& target, T by) {
1123  return detail::increment_by<T>{target, std::move(by)};
1124 }
1125 
1126 /*************************************************************************/
1131 template<class T>
1132 inline detail::decrement<T>
1133 decrement(T& target) {
1134  return detail::decrement<T>{target};
1135 }
1136 
1137 
1138 
1139 
1140 
1142 /*************************************************************************/
1147 namespace detail {
1148 
1149 
1150 /*************************************************************************/
1155 template<class Derived>
1156 class action_provider
1157 {
1158 private:
1159  //---------------------------------------------------------------
1160  using simple_action = std::function<void()>;
1161  using arg_action = std::function<void(const char*)>;
1162  using index_action = std::function<void(int)>;
1163 
1164  //-----------------------------------------------------
1165  class simple_action_adapter {
1166  public:
1167  simple_action_adapter() = default;
1168  simple_action_adapter(const simple_action& a): action_(a) {}
1169  simple_action_adapter(simple_action&& a): action_(std::move(a)) {}
1170  void operator() (const char*) const { action_(); }
1171  void operator() (int) const { action_(); }
1172  private:
1173  simple_action action_;
1174  };
1175 
1176 
1177 public:
1178  //---------------------------------------------------------------
1181  Derived&
1182  call(arg_action a) {
1183  argActions_.push_back(std::move(a));
1184  return *static_cast<Derived*>(this);
1185  }
1186 
1188  Derived&
1189  call(simple_action a) {
1190  argActions_.push_back(simple_action_adapter(std::move(a)));
1191  return *static_cast<Derived*>(this);
1192  }
1193 
1196  Derived& operator () (arg_action a) { return call(std::move(a)); }
1199  Derived& operator () (simple_action a) { return call(std::move(a)); }
1200 
1201 
1202  //---------------------------------------------------------------
1205  template<class Target>
1206  Derived&
1207  set(Target& t) {
1208  return call(clipp::set(t));
1209  }
1210 
1212  template<class Target, class Value>
1213  Derived&
1214  set(Target& t, Value&& v) {
1215  return call(clipp::set(t, std::forward<Value>(v)));
1216  }
1217 
1218 
1219  //---------------------------------------------------------------
1223  Derived&
1224  if_repeated(simple_action a) {
1225  repeatActions_.push_back(simple_action_adapter{std::move(a)});
1226  return *static_cast<Derived*>(this);
1227  }
1232  Derived&
1233  if_repeated(index_action a) {
1234  repeatActions_.push_back(std::move(a));
1235  return *static_cast<Derived*>(this);
1236  }
1237 
1238 
1239  //---------------------------------------------------------------
1243  Derived&
1244  if_missing(simple_action a) {
1245  missingActions_.push_back(simple_action_adapter{std::move(a)});
1246  return *static_cast<Derived*>(this);
1247  }
1252  Derived&
1253  if_missing(index_action a) {
1254  missingActions_.push_back(std::move(a));
1255  return *static_cast<Derived*>(this);
1256  }
1257 
1258 
1259  //---------------------------------------------------------------
1263  Derived&
1264  if_blocked(simple_action a) {
1265  blockedActions_.push_back(simple_action_adapter{std::move(a)});
1266  return *static_cast<Derived*>(this);
1267  }
1273  Derived&
1274  if_blocked(index_action a) {
1275  blockedActions_.push_back(std::move(a));
1276  return *static_cast<Derived*>(this);
1277  }
1278 
1279 
1280  //---------------------------------------------------------------
1284  Derived&
1285  if_conflicted(simple_action a) {
1286  conflictActions_.push_back(simple_action_adapter{std::move(a)});
1287  return *static_cast<Derived*>(this);
1288  }
1294  Derived&
1295  if_conflicted(index_action a) {
1296  conflictActions_.push_back(std::move(a));
1297  return *static_cast<Derived*>(this);
1298  }
1299 
1300 
1301  //---------------------------------------------------------------
1305  template<class T, class... Ts>
1306  Derived&
1307  target(T&& t, Ts&&... ts) {
1308  target(std::forward<T>(t));
1309  target(std::forward<Ts>(ts)...);
1310  return *static_cast<Derived*>(this);
1311  }
1312 
1314  template<class T, class = typename std::enable_if<
1315  !std::is_fundamental<typename std::decay<T>::type>() &&
1316  (traits::is_callable<T,void()>() ||
1317  traits::is_callable<T,void(const char*)>() )
1318  >::type>
1319  Derived&
1320  target(T&& t) {
1321  call(std::forward<T>(t));
1322  return *static_cast<Derived*>(this);
1323  }
1324 
1327  template<class T, class = typename std::enable_if<
1328  std::is_fundamental<typename std::decay<T>::type>() ||
1329  (!traits::is_callable<T,void()>() &&
1330  !traits::is_callable<T,void(const char*)>() )
1331  >::type>
1332  Derived&
1333  target(T& t) {
1334  set(t);
1335  return *static_cast<Derived*>(this);
1336  }
1337 
1338  //TODO remove ugly empty param list overload
1339  Derived&
1340  target() {
1341  return *static_cast<Derived*>(this);
1342  }
1343 
1344 
1345  //---------------------------------------------------------------
1347  template<class Target>
1348  inline friend Derived&
1349  operator << (Target&& t, Derived& p) {
1350  p.target(std::forward<Target>(t));
1351  return p;
1352  }
1354  template<class Target>
1355  inline friend Derived&&
1356  operator << (Target&& t, Derived&& p) {
1357  p.target(std::forward<Target>(t));
1358  return std::move(p);
1359  }
1360 
1361  //-----------------------------------------------------
1363  template<class Target>
1364  inline friend Derived&
1365  operator >> (Derived& p, Target&& t) {
1366  p.target(std::forward<Target>(t));
1367  return p;
1368  }
1370  template<class Target>
1371  inline friend Derived&&
1372  operator >> (Derived&& p, Target&& t) {
1373  p.target(std::forward<Target>(t));
1374  return std::move(p);
1375  }
1376 
1377 
1378  //---------------------------------------------------------------
1380  void execute_actions(const arg_string& arg) const {
1381  int i = 0;
1382  for(const auto& a : argActions_) {
1383  ++i;
1384  a(arg.c_str());
1385  }
1386  }
1387 
1389  void notify_repeated(arg_index idx) const {
1390  for(const auto& a : repeatActions_) a(idx);
1391  }
1393  void notify_missing(arg_index idx) const {
1394  for(const auto& a : missingActions_) a(idx);
1395  }
1397  void notify_blocked(arg_index idx) const {
1398  for(const auto& a : blockedActions_) a(idx);
1399  }
1401  void notify_conflict(arg_index idx) const {
1402  for(const auto& a : conflictActions_) a(idx);
1403  }
1404 
1405 private:
1406  //---------------------------------------------------------------
1407  std::vector<arg_action> argActions_;
1408  std::vector<index_action> repeatActions_;
1409  std::vector<index_action> missingActions_;
1410  std::vector<index_action> blockedActions_;
1411  std::vector<index_action> conflictActions_;
1412 };
1413 
1414 
1415 
1416 
1417 
1418 
1419 /*************************************************************************/
1424 template<class Derived>
1425 class token
1426 {
1427 public:
1428  //---------------------------------------------------------------
1429  using doc_string = clipp::doc_string;
1430 
1431 
1432  //---------------------------------------------------------------
1434  const doc_string& doc() const noexcept {
1435  return doc_;
1436  }
1439  Derived& doc(const doc_string& txt) {
1440  doc_ = txt;
1441  return *static_cast<Derived*>(this);
1442  }
1443 
1445  Derived& doc(doc_string&& txt) {
1446  doc_ = std::move(txt);
1447  return *static_cast<Derived*>(this);
1448  }
1449 
1450 
1451  //---------------------------------------------------------------
1453  bool repeatable() const noexcept {
1454  return repeatable_;
1455  }
1456 
1458  Derived& repeatable(bool yes) noexcept {
1459  repeatable_ = yes;
1460  return *static_cast<Derived*>(this);
1461  }
1462 
1463 
1464  //---------------------------------------------------------------
1466  bool blocking() const noexcept {
1467  return blocking_;
1468  }
1469 
1471  Derived& blocking(bool yes) noexcept {
1472  blocking_ = yes;
1473  return *static_cast<Derived*>(this);
1474  }
1475 
1476 
1477 private:
1478  //---------------------------------------------------------------
1480  bool repeatable_ = false;
1481  bool blocking_ = false;
1482 };
1483 
1484 
1485 
1486 
1487 /*************************************************************************/
1492 template<class T>
1493 inline T&
1494 operator % (doc_string docstr, token<T>& p)
1495 {
1496  return p.doc(std::move(docstr));
1497 }
1498 //---------------------------------------------------------
1499 template<class T>
1500 inline T&&
1501 operator % (doc_string docstr, token<T>&& p)
1503  return std::move(p.doc(std::move(docstr)));
1504 }
1505 
1506 //---------------------------------------------------------
1507 template<class T>
1508 inline T&
1510 {
1511  return p.doc(std::move(docstr));
1512 }
1513 //---------------------------------------------------------
1514 template<class T>
1515 inline T&&
1516 operator % (token<T>&& p, doc_string docstr)
1518  return std::move(p.doc(std::move(docstr)));
1519 }
1520 
1521 
1522 
1523 
1524 /*************************************************************************/
1529 template<class T>
1530 inline T&
1531 doc(doc_string docstr, token<T>& p)
1532 {
1533  return p.doc(std::move(docstr));
1534 }
1535 //---------------------------------------------------------
1536 template<class T>
1537 inline T&&
1538 doc(doc_string docstr, token<T>&& p)
1540  return std::move(p.doc(std::move(docstr)));
1541 }
1542 
1543 
1544 
1545 } // namespace detail
1547 
1548 
1549 /*************************************************************************/
1554 namespace match {
1555 
1556 
1557 /*************************************************************************/
1562 inline bool
1563 any(const arg_string&) { return true; }
1564 
1565 /*************************************************************************/
1570 inline bool
1571 none(const arg_string&) { return false; }
1572 
1573 
1574 
1575 /*************************************************************************/
1580 inline bool
1581 nonempty(const arg_string& s) {
1582  return !s.empty();
1583 }
1584 
1585 
1586 
1587 /*************************************************************************/
1593 inline bool
1594 alphanumeric(const arg_string& s) {
1595  if(s.empty()) return false;
1596  return std::all_of(s.begin(), s.end(), [](char c) {return std::isalnum(c); });
1597 }
1598 
1599 
1600 
1601 /*************************************************************************/
1607 inline bool
1608 alphabetic(const arg_string& s) {
1609  return std::all_of(s.begin(), s.end(), [](char c) {return std::isalpha(c); });
1610 }
1611 
1612 
1613 
1614 /*************************************************************************/
1621 class numbers
1622 {
1623 public:
1624  explicit
1625  numbers(char decimalPoint = '.',
1626  char digitSeparator = ' ',
1627  char exponentSeparator = 'e')
1628  :
1629  decpoint_{decimalPoint}, separator_{digitSeparator},
1630  exp_{exponentSeparator}
1631  {}
1632 
1634  return str::first_number_match(s, separator_, decpoint_, exp_);
1635  }
1636 
1637 private:
1638  char decpoint_;
1639  char separator_;
1640  char exp_;
1641 };
1642 
1643 
1644 
1645 /*************************************************************************/
1651 class integers {
1652 public:
1653  explicit
1654  integers(char digitSeparator = ' '): separator_{digitSeparator} {}
1655 
1656  subrange operator () (const arg_string& s) const {
1657  return str::first_integer_match(s, separator_);
1658  }
1660 private:
1661  char separator_;
1662 };
1663 
1665 
1666 /*************************************************************************/
1672 class positive_integers {
1673 public:
1674  explicit
1675  positive_integers(char digitSeparator = ' '): separator_{digitSeparator} {}
1676 
1677  subrange operator () (const arg_string& s) const {
1678  auto match = str::first_integer_match(s, separator_);
1679  if(!match) return subrange{};
1680  if(s[match.at()] == '-') return subrange{};
1681  return match;
1682  }
1684 private:
1685  char separator_;
1686 };
1687 
1688 
1689 
1690 /*************************************************************************/
1696 class substring
1697 {
1698 public:
1699  explicit
1700  substring(arg_string str): str_{std::move(str)} {}
1701 
1702  subrange operator () (const arg_string& s) const {
1703  return str::substring_match(s, str_);
1704  }
1705 
1706 private:
1707  arg_string str_;
1708 };
1709 
1711 
1712 /*************************************************************************/
1718 class prefix {
1719 public:
1720  explicit
1721  prefix(arg_string p): prefix_{std::move(p)} {}
1722 
1723  bool operator () (const arg_string& s) const {
1724  return s.find(prefix_) == 0;
1725  }
1727 private:
1728  arg_string prefix_;
1729 };
1730 
1732 
1733 /*************************************************************************/
1739 class prefix_not {
1740 public:
1741  explicit
1742  prefix_not(arg_string p): prefix_{std::move(p)} {}
1743 
1744  bool operator () (const arg_string& s) const {
1745  return s.find(prefix_) != 0;
1746  }
1748 private:
1749  arg_string prefix_;
1750 };
1751 
1754 using noprefix = prefix_not;
1755 
1756 
1757 
1758 /*************************************************************************/
1764 class length {
1765 public:
1766  explicit
1767  length(std::size_t exact):
1768  min_{exact}, max_{exact}
1769  {}
1770 
1771  explicit
1772  length(std::size_t min, std::size_t max):
1773  min_{min}, max_{max}
1774  {}
1776  bool operator () (const arg_string& s) const {
1777  return s.size() >= min_ && s.size() <= max_;
1778  }
1779 
1780 private:
1781  std::size_t min_;
1782  std::size_t max_;
1783 };
1785 
1786 /*************************************************************************/
1792 inline length min_length(std::size_t min)
1793 {
1794  return length{min, arg_string::npos-1};
1795 }
1796 
1797 /*************************************************************************/
1803 inline length max_length(std::size_t max)
1804 {
1805  return length{0, max};
1806 }
1807 
1808 
1809 } // namespace match
1810 
1812 
1813 
1814 
1815 /*************************************************************************/
1820 class parameter :
1821  public detail::token<parameter>,
1822  public detail::action_provider<parameter>
1823 {
1824  class predicate_adapter {
1825  public:
1826  explicit
1827  predicate_adapter(match_predicate pred): match_{std::move(pred)} {}
1829  subrange operator () (const arg_string& arg) const {
1830  return match_(arg) ? subrange{0,arg.size()} : subrange{};
1831  }
1832 
1833  private:
1834  match_predicate match_;
1835  };
1836 
1837 public:
1838  //---------------------------------------------------------------
1840  parameter():
1841  flags_{},
1842  matcher_{predicate_adapter{match::none}},
1843  label_{}, required_{false}
1844  {}
1845 
1847  template<class... Strings>
1848  explicit
1849  parameter(arg_string str, Strings&&... strs):
1850  flags_{},
1851  matcher_{predicate_adapter{match::none}},
1852  label_{}, required_{false}
1853  {
1854  add_flags(std::move(str), std::forward<Strings>(strs)...);
1855  }
1856 
1858  explicit
1859  parameter(const arg_list& flaglist):
1860  flags_{},
1861  matcher_{predicate_adapter{match::none}},
1862  label_{}, required_{false}
1863  {
1864  add_flags(flaglist);
1865  }
1866 
1867  //-----------------------------------------------------
1871  explicit
1872  parameter(match_predicate filter):
1873  flags_{},
1874  matcher_{predicate_adapter{std::move(filter)}},
1875  label_{}, required_{false}
1876  {}
1877 
1881  explicit
1882  parameter(match_function filter):
1883  flags_{},
1884  matcher_{std::move(filter)},
1885  label_{}, required_{false}
1886  {}
1887 
1888 
1889  //---------------------------------------------------------------
1891  bool
1892  required() const noexcept {
1893  return required_;
1894  }
1895 
1897  parameter&
1898  required(bool yes) noexcept {
1899  required_ = yes;
1900  return *this;
1901  }
1902 
1903 
1904  //---------------------------------------------------------------
1908  const doc_string&
1909  label() const {
1910  return label_;
1911  }
1912 
1916  parameter&
1917  label(const doc_string& lbl) {
1918  label_ = lbl;
1919  return *this;
1920  }
1921 
1926  label(doc_string&& lbl) {
1927  label_ = lbl;
1928  return *this;
1929  }
1930 
1931 
1932  //---------------------------------------------------------------
1936  subrange
1937  match(const arg_string& arg) const
1938  {
1939  if(arg.empty()) return subrange{};
1940 
1941  if(flags_.empty()) {
1942  return matcher_(arg);
1943  }
1944  else {
1945  if(std::find(flags_.begin(), flags_.end(), arg) != flags_.end()) {
1946  return subrange{0,arg.size()};
1947  }
1948  return str::longest_prefix_match(arg, flags_);
1949  }
1950  }
1951 
1952 
1953  //---------------------------------------------------------------
1955  const arg_list&
1956  flags() const noexcept {
1957  return flags_;
1958  }
1959 
1961  const match_function&
1962  matcher() const noexcept {
1963  return matcher_;
1964  }
1965 
1966 
1967  //---------------------------------------------------------------
1969  inline friend parameter&
1970  with_prefix(const arg_string& prefix, parameter& p)
1971  {
1972  if(prefix.empty() || p.flags().empty()) return p;
1973 
1974  for(auto& f : p.flags_) {
1975  if(f.find(prefix) != 0) f.insert(0, prefix);
1976  }
1977  return p;
1978  }
1979 
1980 
1983  inline friend parameter&
1985  const arg_string& shortpfx, const arg_string& longpfx,
1986  parameter& p)
1987  {
1988  if(shortpfx.empty() && longpfx.empty()) return p;
1989  if(p.flags().empty()) return p;
1990 
1991  for(auto& f : p.flags_) {
1992  if(f.size() == 1) {
1993  if(f.find(shortpfx) != 0) f.insert(0, shortpfx);
1994  } else {
1995  if(f.find(longpfx) != 0) f.insert(0, longpfx);
1996  }
1997  }
1998  return p;
1999  }
2000 
2001 private:
2002  //---------------------------------------------------------------
2003  void add_flags(arg_string str) {
2004  //empty flags are not allowed
2005  str::remove_ws(str);
2006  if(!str.empty()) flags_.push_back(std::move(str));
2007  }
2008 
2009  //---------------------------------------------------------------
2010  void add_flags(const arg_list& strs) {
2011  if(strs.empty()) return;
2012  flags_.reserve(flags_.size() + strs.size());
2013  for(const auto& s : strs) add_flags(s);
2014  }
2015 
2016  template<class String1, class String2, class... Strings>
2017  void
2018  add_flags(String1&& s1, String2&& s2, Strings&&... ss) {
2019  flags_.reserve(2 + sizeof...(ss));
2020  add_flags(std::forward<String1>(s1));
2021  add_flags(std::forward<String2>(s2), std::forward<Strings>(ss)...);
2022  }
2023 
2024  arg_list flags_;
2025  match_function matcher_;
2026  doc_string label_;
2027  bool required_ = false;
2028 };
2029 
2030 
2031 
2032 
2033 /*************************************************************************/
2038 template<class String, class... Strings>
2039 inline parameter
2040 command(String&& flag, Strings&&... flags)
2041 {
2042  return parameter{std::forward<String>(flag), std::forward<Strings>(flags)...}
2043  .required(true).blocking(true).repeatable(false);
2044 }
2045 
2046 
2047 
2048 /*************************************************************************/
2053 template<class String, class... Strings>
2054 inline parameter
2055 required(String&& flag, Strings&&... flags)
2056 {
2057  return parameter{std::forward<String>(flag), std::forward<Strings>(flags)...}
2058  .required(true).blocking(false).repeatable(false);
2059 }
2060 
2061 
2062 
2063 /*************************************************************************/
2068 template<class String, class... Strings>
2069 inline parameter
2070 option(String&& flag, Strings&&... flags)
2071 {
2072  return parameter{std::forward<String>(flag), std::forward<Strings>(flags)...}
2073  .required(false).blocking(false).repeatable(false);
2074 }
2075 
2076 
2077 
2078 /*************************************************************************/
2084 template<class... Targets>
2085 inline parameter
2086 value(const doc_string& label, Targets&&... tgts)
2087 {
2088  return parameter{match::nonempty}
2089  .label(label)
2090  .target(std::forward<Targets>(tgts)...)
2091  .required(true).blocking(true).repeatable(false);
2092 }
2093 
2094 template<class Filter, class... Targets, class = typename std::enable_if<
2095  traits::is_callable<Filter,bool(const char*)>::value ||
2096  traits::is_callable<Filter,subrange(const char*)>::value>::type>
2097 inline parameter
2098 value(Filter&& filter, doc_string label, Targets&&... tgts)
2099 {
2100  return parameter{std::forward<Filter>(filter)}
2101  .label(label)
2102  .target(std::forward<Targets>(tgts)...)
2103  .required(true).blocking(true).repeatable(false);
2104 }
2105 
2107 
2108 /*************************************************************************/
2114 template<class... Targets>
2115 inline parameter
2116 values(const doc_string& label, Targets&&... tgts)
2117 {
2118  return parameter{match::nonempty}
2119  .label(label)
2120  .target(std::forward<Targets>(tgts)...)
2121  .required(true).blocking(true).repeatable(true);
2122 }
2123 
2124 template<class Filter, class... Targets, class = typename std::enable_if<
2125  traits::is_callable<Filter,bool(const char*)>::value ||
2126  traits::is_callable<Filter,subrange(const char*)>::value>::type>
2127 inline parameter
2128 values(Filter&& filter, doc_string label, Targets&&... tgts)
2129 {
2130  return parameter{std::forward<Filter>(filter)}
2131  .label(label)
2132  .target(std::forward<Targets>(tgts)...)
2133  .required(true).blocking(true).repeatable(true);
2134 }
2135 
2137 
2138 /*************************************************************************/
2144 template<class... Targets>
2145 inline parameter
2146 opt_value(const doc_string& label, Targets&&... tgts)
2147 {
2148  return parameter{match::nonempty}
2149  .label(label)
2150  .target(std::forward<Targets>(tgts)...)
2151  .required(false).blocking(false).repeatable(false);
2152 }
2153 
2154 template<class Filter, class... Targets, class = typename std::enable_if<
2155  traits::is_callable<Filter,bool(const char*)>::value ||
2156  traits::is_callable<Filter,subrange(const char*)>::value>::type>
2157 inline parameter
2158 opt_value(Filter&& filter, doc_string label, Targets&&... tgts)
2159 {
2160  return parameter{std::forward<Filter>(filter)}
2161  .label(label)
2162  .target(std::forward<Targets>(tgts)...)
2163  .required(false).blocking(false).repeatable(false);
2164 }
2165 
2167 
2168 /*************************************************************************/
2174 template<class... Targets>
2175 inline parameter
2176 opt_values(const doc_string& label, Targets&&... tgts)
2177 {
2178  return parameter{match::nonempty}
2179  .label(label)
2180  .target(std::forward<Targets>(tgts)...)
2181  .required(false).blocking(false).repeatable(true);
2182 }
2183 
2184 template<class Filter, class... Targets, class = typename std::enable_if<
2185  traits::is_callable<Filter,bool(const char*)>::value ||
2186  traits::is_callable<Filter,subrange(const char*)>::value>::type>
2187 inline parameter
2188 opt_values(Filter&& filter, doc_string label, Targets&&... tgts)
2189 {
2190  return parameter{std::forward<Filter>(filter)}
2191  .label(label)
2192  .target(std::forward<Targets>(tgts)...)
2193  .required(false).blocking(false).repeatable(true);
2194 }
2195 
2197 
2198 /*************************************************************************/
2204 template<class... Targets>
2205 inline parameter
2206 word(const doc_string& label, Targets&&... tgts)
2207 {
2209  .label(label)
2210  .target(std::forward<Targets>(tgts)...)
2211  .required(true).blocking(true).repeatable(false);
2212 }
2213 
2215 
2216 /*************************************************************************/
2222 template<class... Targets>
2223 inline parameter
2224 words(const doc_string& label, Targets&&... tgts)
2225 {
2227  .label(label)
2228  .target(std::forward<Targets>(tgts)...)
2229  .required(true).blocking(true).repeatable(true);
2230 }
2231 
2233 
2234 /*************************************************************************/
2240 template<class... Targets>
2241 inline parameter
2242 opt_word(const doc_string& label, Targets&&... tgts)
2243 {
2245  .label(label)
2246  .target(std::forward<Targets>(tgts)...)
2247  .required(false).blocking(false).repeatable(false);
2248 }
2249 
2251 
2252 /*************************************************************************/
2258 template<class... Targets>
2259 inline parameter
2260 opt_words(const doc_string& label, Targets&&... tgts)
2261 {
2263  .label(label)
2264  .target(std::forward<Targets>(tgts)...)
2265  .required(false).blocking(false).repeatable(true);
2266 }
2267 
2269 
2270 /*************************************************************************/
2276 template<class... Targets>
2277 inline parameter
2278 number(const doc_string& label, Targets&&... tgts)
2279 {
2280  return parameter{match::numbers{}}
2281  .label(label)
2282  .target(std::forward<Targets>(tgts)...)
2283  .required(true).blocking(true).repeatable(false);
2284 }
2285 
2287 
2288 /*************************************************************************/
2294 template<class... Targets>
2295 inline parameter
2296 numbers(const doc_string& label, Targets&&... tgts)
2297 {
2298  return parameter{match::numbers{}}
2299  .label(label)
2300  .target(std::forward<Targets>(tgts)...)
2301  .required(true).blocking(true).repeatable(true);
2302 }
2303 
2305 
2306 /*************************************************************************/
2312 template<class... Targets>
2313 inline parameter
2314 opt_number(const doc_string& label, Targets&&... tgts)
2315 {
2316  return parameter{match::numbers{}}
2317  .label(label)
2318  .target(std::forward<Targets>(tgts)...)
2319  .required(false).blocking(false).repeatable(false);
2320 }
2321 
2323 
2324 /*************************************************************************/
2330 template<class... Targets>
2331 inline parameter
2332 opt_numbers(const doc_string& label, Targets&&... tgts)
2333 {
2334  return parameter{match::numbers{}}
2335  .label(label)
2336  .target(std::forward<Targets>(tgts)...)
2337  .required(false).blocking(false).repeatable(true);
2338 }
2339 
2341 
2342 /*************************************************************************/
2348 template<class... Targets>
2349 inline parameter
2350 integer(const doc_string& label, Targets&&... tgts)
2351 {
2352  return parameter{match::integers{}}
2353  .label(label)
2354  .target(std::forward<Targets>(tgts)...)
2355  .required(true).blocking(true).repeatable(false);
2356 }
2357 
2359 
2360 /*************************************************************************/
2366 template<class... Targets>
2367 inline parameter
2368 integers(const doc_string& label, Targets&&... tgts)
2369 {
2370  return parameter{match::integers{}}
2371  .label(label)
2372  .target(std::forward<Targets>(tgts)...)
2373  .required(true).blocking(true).repeatable(true);
2374 }
2375 
2377 
2378 /*************************************************************************/
2384 template<class... Targets>
2385 inline parameter
2386 opt_integer(const doc_string& label, Targets&&... tgts)
2387 {
2388  return parameter{match::integers{}}
2389  .label(label)
2390  .target(std::forward<Targets>(tgts)...)
2391  .required(false).blocking(false).repeatable(false);
2392 }
2393 
2395 
2396 /*************************************************************************/
2402 template<class... Targets>
2403 inline parameter
2404 opt_integers(const doc_string& label, Targets&&... tgts)
2405 {
2406  return parameter{match::integers{}}
2407  .label(label)
2408  .target(std::forward<Targets>(tgts)...)
2409  .required(false).blocking(false).repeatable(true);
2410 }
2411 
2413 
2414 /*************************************************************************/
2419 template<class... Targets>
2420 inline parameter
2421 any_other(Targets&&... tgts)
2422 {
2423  return parameter{match::any}
2424  .target(std::forward<Targets>(tgts)...)
2425  .required(false).blocking(false).repeatable(true);
2426 }
2427 
2428 
2430 
2431 /*************************************************************************/
2437 class group :
2438  public detail::token<group>
2439 {
2440  //---------------------------------------------------------------
2451  template<class Param, class Group>
2452  struct child_t {
2453  enum class type : char {param, group};
2454  public:
2455 
2456  explicit
2457  child_t(const Param& v) : m_{v}, type_{type::param} {}
2458  child_t( Param&& v) noexcept : m_{std::move(v)}, type_{type::param} {}
2459 
2460  explicit
2461  child_t(const Group& g) : m_{g}, type_{type::group} {}
2462  child_t( Group&& g) noexcept : m_{std::move(g)}, type_{type::group} {}
2463 
2464  child_t(const child_t& src): type_{src.type_} {
2465  switch(type_) {
2466  default:
2467  case type::param: new(&m_)data{src.m_.param}; break;
2468  case type::group: new(&m_)data{src.m_.group}; break;
2469  }
2470  }
2471 
2472  child_t(child_t&& src) noexcept : type_{src.type_} {
2473  switch(type_) {
2474  default:
2475  case type::param: new(&m_)data{std::move(src.m_.param)}; break;
2476  case type::group: new(&m_)data{std::move(src.m_.group)}; break;
2477  }
2478  }
2479 
2480  child_t& operator = (const child_t& src) {
2481  destroy_content();
2482  type_ = src.type_;
2483  switch(type_) {
2484  default:
2485  case type::param: new(&m_)data{src.m_.param}; break;
2486  case type::group: new(&m_)data{src.m_.group}; break;
2487  }
2488  return *this;
2489  }
2490 
2491  child_t& operator = (child_t&& src) noexcept {
2492  destroy_content();
2493  type_ = src.type_;
2494  switch(type_) {
2495  default:
2496  case type::param: new(&m_)data{std::move(src.m_.param)}; break;
2497  case type::group: new(&m_)data{std::move(src.m_.group)}; break;
2498  }
2499  return *this;
2500  }
2501 
2502  ~child_t() {
2503  destroy_content();
2504  }
2505 
2506  const doc_string&
2507  doc() const noexcept {
2508  switch(type_) {
2509  default:
2510  case type::param: return m_.param.doc();
2511  case type::group: return m_.group.doc();
2512  }
2513  }
2514 
2515  bool blocking() const noexcept {
2516  switch(type_) {
2517  case type::param: return m_.param.blocking();
2518  case type::group: return m_.group.blocking();
2519  default: return false;
2520  }
2521  }
2522  bool repeatable() const noexcept {
2523  switch(type_) {
2524  case type::param: return m_.param.repeatable();
2525  case type::group: return m_.group.repeatable();
2526  default: return false;
2527  }
2528  }
2529  bool required() const noexcept {
2530  switch(type_) {
2531  case type::param: return m_.param.required();
2532  case type::group:
2533  return (m_.group.exclusive() && m_.group.all_required() ) ||
2534  (!m_.group.exclusive() && m_.group.any_required() );
2535  default: return false;
2536  }
2537  }
2538  bool exclusive() const noexcept {
2539  switch(type_) {
2540  case type::group: return m_.group.exclusive();
2541  case type::param:
2542  default: return false;
2543  }
2544  }
2545  std::size_t param_count() const noexcept {
2546  switch(type_) {
2547  case type::group: return m_.group.param_count();
2548  case type::param:
2549  default: return std::size_t(1);
2550  }
2551  }
2552  std::size_t depth() const noexcept {
2553  switch(type_) {
2554  case type::group: return m_.group.depth();
2555  case type::param:
2556  default: return std::size_t(0);
2557  }
2558  }
2559 
2560  void execute_actions(const arg_string& arg) const {
2561  switch(type_) {
2562  default:
2563  case type::group: return;
2564  case type::param: m_.param.execute_actions(arg); break;
2565  }
2566 
2567  }
2568 
2569  void notify_repeated(arg_index idx) const {
2570  switch(type_) {
2571  default:
2572  case type::group: return;
2573  case type::param: m_.param.notify_repeated(idx); break;
2574  }
2575  }
2576  void notify_missing(arg_index idx) const {
2577  switch(type_) {
2578  default:
2579  case type::group: return;
2580  case type::param: m_.param.notify_missing(idx); break;
2581  }
2582  }
2583  void notify_blocked(arg_index idx) const {
2584  switch(type_) {
2585  default:
2586  case type::group: return;
2587  case type::param: m_.param.notify_blocked(idx); break;
2588  }
2589  }
2590  void notify_conflict(arg_index idx) const {
2591  switch(type_) {
2592  default:
2593  case type::group: return;
2594  case type::param: m_.param.notify_conflict(idx); break;
2595  }
2596  }
2597 
2598  bool is_param() const noexcept { return type_ == type::param; }
2599  bool is_group() const noexcept { return type_ == type::group; }
2600 
2601  Param& as_param() noexcept { return m_.param; }
2602  Group& as_group() noexcept { return m_.group; }
2603 
2604  const Param& as_param() const noexcept { return m_.param; }
2605  const Group& as_group() const noexcept { return m_.group; }
2606 
2607  private:
2608  void destroy_content() {
2609  switch(type_) {
2610  default:
2611  case type::param: m_.param.~Param(); break;
2612  case type::group: m_.group.~Group(); break;
2613  }
2614  }
2615 
2616  union data {
2617  data() {}
2618 
2619  data(const Param& v) : param{v} {}
2620  data( Param&& v) noexcept : param{std::move(v)} {}
2621 
2622  data(const Group& g) : group{g} {}
2623  data( Group&& g) noexcept : group{std::move(g)} {}
2624  ~data() {}
2625 
2626  Param param;
2627  Group group;
2628  };
2629 
2630  data m_;
2631  type type_;
2632  };
2633 
2634 
2635 public:
2636  //---------------------------------------------------------------
2637  using child = child_t<parameter,group>;
2638  using value_type = child;
2639 
2640 private:
2641  using children_store = std::vector<child>;
2642 
2643 public:
2644  using const_iterator = children_store::const_iterator;
2645  using iterator = children_store::iterator;
2646  using size_type = children_store::size_type;
2647 
2648 
2649  //---------------------------------------------------------------
2654  {
2655  public:
2656  //-----------------------------------------------------
2657  struct context {
2658  context() = default;
2659  context(const group& p):
2660  parent{&p}, cur{p.begin()}, end{p.end()}
2661  {}
2662  const group* parent = nullptr;
2665  };
2666  using context_list = std::vector<context>;
2668  //-----------------------------------------------------
2669  class memento {
2671  int level_;
2672  context context_;
2673  public:
2674  int level() const noexcept { return level_; }
2675  const child* param() const noexcept { return &(*context_.cur); }
2676  };
2679 
2680  explicit
2681  depth_first_traverser(const group& cur): stack_{} {
2682  if(!cur.empty()) stack_.emplace_back(cur);
2683  }
2684 
2685  explicit operator bool() const noexcept {
2686  return !stack_.empty();
2687  }
2688 
2689  int level() const noexcept {
2690  return int(stack_.size());
2691  }
2692 
2693  bool is_first_in_group() const noexcept {
2694  if(stack_.empty()) return false;
2695  return (stack_.back().cur == stack_.back().parent->begin());
2696  }
2698  bool is_last_in_group() const noexcept {
2699  if(stack_.empty()) return false;
2700  return (stack_.back().cur+1 == stack_.back().end);
2701  }
2702 
2703  bool is_last_in_path() const noexcept {
2704  if(stack_.empty()) return false;
2705  for(const auto& t : stack_) {
2706  if(t.cur+1 != t.end) return false;
2707  }
2708  const auto& top = stack_.back();
2709  //if we have to descend into group on next ++ => not last in path
2710  if(top.cur->is_group()) return false;
2711  return true;
2712  }
2713 
2715  bool is_alternative(int minlevel = 0) const noexcept {
2716  if(stack_.empty()) return false;
2717  if(minlevel > 0) minlevel -= 1;
2718  if(minlevel >= int(stack_.size())) return false;
2719  return std::any_of(stack_.begin() + minlevel, stack_.end(),
2720  [](const context& c) { return c.parent->exclusive(); });
2721  }
2722 
2724  bool is_repeatable(int minlevel = 0) const noexcept {
2725  if(stack_.empty()) return false;
2726  if(stack_.back().cur->repeatable()) return true;
2727  if(minlevel > 0) minlevel -= 1;
2728  if(minlevel >= int(stack_.size())) return false;
2729  return std::any_of(stack_.begin() + minlevel, stack_.end(),
2730  [](const context& c) { return c.parent->repeatable(); });
2731  }
2733  bool joinable() const noexcept {
2734  if(stack_.empty()) return false;
2735  return std::any_of(stack_.begin(), stack_.end(),
2736  [](const context& c) { return c.parent->joinable(); });
2737  }
2738 
2739  const context_list&
2740  stack() const {
2741  return stack_;
2742  }
2743 
2745  const group*
2746  repeat_group() const noexcept {
2747  auto i = std::find_if(stack_.rbegin(), stack_.rend(),
2748  [](const context& c) { return c.parent->repeatable(); });
2749 
2750  return i != stack_.rend() ? i->parent : nullptr;
2751  }
2752 
2754  const group*
2755  join_group() const noexcept {
2756  auto i = std::find_if(stack_.begin(), stack_.end(),
2757  [](const context& c) { return c.parent->joinable(); });
2758  return i != stack_.end() ? i->parent : nullptr;
2759  }
2760 
2761  const group* root() const noexcept {
2762  return stack_.empty() ? nullptr : stack_.front().parent;
2763  }
2764 
2766  arg_string common_flag_prefix() const noexcept {
2767  if(stack_.empty()) return "";
2768  auto g = join_group();
2769  return g ? g->common_flag_prefix() : arg_string("");
2770  }
2771 
2772  const child&
2773  operator * () const noexcept {
2774  return *stack_.back().cur;
2775  }
2776 
2777  const child*
2778  operator -> () const noexcept {
2779  return &(*stack_.back().cur);
2780  }
2782  const group&
2783  parent() const noexcept {
2784  return *(stack_.back().parent);
2785  }
2787 
2790  operator ++ () {
2791  if(stack_.empty()) return *this;
2792  //at group -> decend into group
2793  if(stack_.back().cur->is_group()) {
2794  stack_.emplace_back(stack_.back().cur->as_group());
2795  }
2796  else {
2797  next_sibling();
2798  }
2799  return *this;
2800  }
2801 
2804  next_sibling() {
2805  if(stack_.empty()) return *this;
2806  ++stack_.back().cur;
2807  //at the end of current group?
2808  while(stack_.back().cur == stack_.back().end) {
2809  //go to parent
2810  stack_.pop_back();
2811  if(stack_.empty()) return *this;
2812  //go to next sibling in parent
2813  ++stack_.back().cur;
2814  }
2815  return *this;
2816  }
2817 
2821  if(stack_.empty()) return *this;
2822  stack_.back().cur = stack_.back().end-1;
2823  next_sibling();
2824  return *this;
2825  }
2826 
2830  next_alternative() {
2831  if(stack_.empty()) return *this;
2832 
2833  //find first exclusive group (from the top of the stack!)
2834  auto i = std::find_if(stack_.rbegin(), stack_.rend(),
2835  [](const context& c) { return c.parent->exclusive(); });
2836  if(i == stack_.rend()) return *this;
2837 
2838  stack_.erase(i.base(), stack_.end());
2839  next_sibling();
2840  return *this;
2841  }
2842 
2847  back_to_parent() {
2848  if(stack_.empty()) return *this;
2849  stack_.pop_back();
2850  return *this;
2851  }
2852 
2857  skip_siblings() {
2858  if(stack_.empty()) return *this;
2859  //future increments won't visit subsequent siblings:
2860  stack_.back().end = stack_.back().cur+1;
2861  return *this;
2862  }
2863 
2869  skip_alternatives() {
2870  if(stack_.empty()) return *this;
2871 
2872  //exclude all other alternatives in surrounding groups
2873  //by making their current position the last one
2874  for(auto& c : stack_) {
2875  if(c.parent && c.parent->exclusive() && c.cur < c.end)
2876  c.end = c.cur+1;
2877  }
2878 
2879  return *this;
2880  }
2881 
2882  void invalidate() {
2883  stack_.clear();
2884  }
2885 
2886  inline friend bool operator == (const depth_first_traverser& a,
2887  const depth_first_traverser& b)
2888  {
2889  if(a.stack_.empty() || b.stack_.empty()) return false;
2891  //parents not the same -> different position
2892  if(a.stack_.back().parent != b.stack_.back().parent) return false;
2893 
2894  bool aEnd = a.stack_.back().cur == a.stack_.back().end;
2895  bool bEnd = b.stack_.back().cur == b.stack_.back().end;
2896  //either both at the end of the same parent => same position
2897  if(aEnd && bEnd) return true;
2898  //or only one at the end => not at the same position
2899  if(aEnd || bEnd) return false;
2900  return std::addressof(*a.stack_.back().cur) ==
2901  std::addressof(*b.stack_.back().cur);
2902  }
2903  inline friend bool operator != (const depth_first_traverser& a,
2904  const depth_first_traverser& b)
2905  {
2906  return !(a == b);
2907  }
2908 
2909  memento
2910  undo_point() const {
2912  m.level_ = int(stack_.size());
2913  if(!stack_.empty()) m.context_ = stack_.back();
2914  return m;
2915  }
2916 
2917  void undo(const memento& m) {
2918  if(m.level_ < 1) return;
2919  if(m.level_ <= int(stack_.size())) {
2920  stack_.erase(stack_.begin() + m.level_, stack_.end());
2921  stack_.back() = m.context_;
2922  }
2923  else if(stack_.empty() && m.level_ == 1) {
2924  stack_.push_back(m.context_);
2925  }
2926  }
2927 
2928  private:
2929  context_list stack_;
2930  };
2931 
2932 
2933  //---------------------------------------------------------------
2934  group() = default;
2935 
2936  template<class Param, class... Params>
2937  explicit
2938  group(doc_string docstr, Param param, Params... params):
2939  children_{}, exclusive_{false}, joinable_{false}, scoped_{true}
2940  {
2941  doc(std::move(docstr));
2942  push_back(std::move(param), std::move(params)...);
2943  }
2944 
2945  template<class... Params>
2946  explicit
2947  group(parameter param, Params... params):
2948  children_{}, exclusive_{false}, joinable_{false}, scoped_{true}
2949  {
2950  push_back(std::move(param), std::move(params)...);
2951  }
2952 
2953  template<class P2, class... Ps>
2954  explicit
2955  group(group p1, P2 p2, Ps... ps):
2956  children_{}, exclusive_{false}, joinable_{false}, scoped_{true}
2957  {
2958  push_back(std::move(p1), std::move(p2), std::move(ps)...);
2959  }
2960 
2961 
2962  //-----------------------------------------------------
2963  group(const group&) = default;
2964  group(group&&) = default;
2965 
2966 
2967  //---------------------------------------------------------------
2968  group& operator = (const group&) = default;
2969  group& operator = (group&&) = default;
2970 
2972  //---------------------------------------------------------------
2976  group& joinable(bool yes) {
2977  joinable_ = yes;
2978  return *this;
2979  }
2980 
2984  bool joinable() const noexcept {
2985  return joinable_;
2986  }
2987 
2988 
2989  //---------------------------------------------------------------
2994  group& scoped(bool yes) {
2995  scoped_ = yes;
2996  return *this;
2997  }
2998 
3002  bool scoped() const noexcept
3003  {
3004  return scoped_;
3005  }
3006 
3007 
3008  //---------------------------------------------------------------
3010  group& exclusive(bool yes) {
3011  exclusive_ = yes;
3012  return *this;
3013  }
3015  bool exclusive() const noexcept {
3016  return exclusive_;
3017  }
3019 
3020  //---------------------------------------------------------------
3022  bool any_required() const
3023  {
3024  return std::any_of(children_.begin(), children_.end(),
3025  [](const child& n){ return n.required(); });
3026  }
3028  bool all_required() const
3029  {
3030  return std::all_of(children_.begin(), children_.end(),
3031  [](const child& n){ return n.required(); });
3032  }
3033 
3034 
3035  //---------------------------------------------------------------
3037  bool any_optional() const {
3038  return !all_required();
3039  }
3041  bool all_optional() const {
3042  return !any_required();
3043  }
3044 
3046  //---------------------------------------------------------------
3048  bool blocking() const noexcept {
3050  }
3051  //-----------------------------------------------------
3053  group& blocking(bool yes) {
3054  return token<group>::blocking(yes);
3055  }
3057  //---------------------------------------------------------------
3059  bool any_blocking() const
3060  {
3061  return std::any_of(children_.begin(), children_.end(),
3062  [](const child& n){ return n.blocking(); });
3063  }
3064  //---------------------------------------------------------------
3066  bool all_blocking() const
3067  {
3068  return std::all_of(children_.begin(), children_.end(),
3069  [](const child& n){ return n.blocking(); });
3070  }
3071 
3072 
3073  //---------------------------------------------------------------
3075  bool any_flagless() const
3076  {
3077  return std::any_of(children_.begin(), children_.end(),
3078  [](const child& p){
3079  return p.is_param() && p.as_param().flags().empty();
3080  });
3081  }
3083  bool all_flagless() const
3084  {
3085  return std::all_of(children_.begin(), children_.end(),
3086  [](const child& p){
3087  return p.is_param() && p.as_param().flags().empty();
3088  });
3089  }
3090 
3092  //---------------------------------------------------------------
3094  group&
3095  push_back(const parameter& v) {
3096  children_.emplace_back(v);
3097  return *this;
3098  }
3099  //-----------------------------------------------------
3101  group&
3102  push_back(parameter&& v) {
3103  children_.emplace_back(std::move(v));
3104  return *this;
3105  }
3106  //-----------------------------------------------------
3108  group&
3109  push_back(const group& g) {
3110  children_.emplace_back(g);
3111  return *this;
3112  }
3113  //-----------------------------------------------------
3115  group&
3116  push_back(group&& g) {
3117  children_.emplace_back(std::move(g));
3118  return *this;
3119  }
3120 
3121 
3122  //-----------------------------------------------------
3124  template<class Param1, class Param2, class... Params>
3125  group&
3126  push_back(Param1&& param1, Param2&& param2, Params&&... params)
3127  {
3128  children_.reserve(children_.size() + 2 + sizeof...(params));
3129  push_back(std::forward<Param1>(param1));
3130  push_back(std::forward<Param2>(param2), std::forward<Params>(params)...);
3131  return *this;
3132  }
3133 
3135  //---------------------------------------------------------------
3137  group&
3138  push_front(const parameter& v) {
3139  children_.emplace(children_.begin(), v);
3140  return *this;
3141  }
3142  //-----------------------------------------------------
3144  group&
3145  push_front(parameter&& v) {
3146  children_.emplace(children_.begin(), std::move(v));
3147  return *this;
3148  }
3149  //-----------------------------------------------------
3151  group&
3152  push_front(const group& g) {
3153  children_.emplace(children_.begin(), g);
3154  return *this;
3155  }
3156  //-----------------------------------------------------
3158  group&
3159  push_front(group&& g) {
3160  children_.emplace(children_.begin(), std::move(g));
3161  return *this;
3162  }
3163 
3164 
3165  //---------------------------------------------------------------
3168  merge(group&& g)
3169  {
3170  children_.insert(children_.end(),
3171  std::make_move_iterator(g.begin()),
3172  std::make_move_iterator(g.end()));
3173  return *this;
3174  }
3175  //-----------------------------------------------------
3177  template<class... Groups>
3178  group&
3179  merge(group&& g1, group&& g2, Groups&&... gs)
3180  {
3181  merge(std::move(g1));
3182  merge(std::move(g2), std::forward<Groups>(gs)...);
3183  return *this;
3184  }
3185 
3186 
3187  //---------------------------------------------------------------
3189  child& operator [] (size_type index) noexcept {
3190  return children_[index];
3191  }
3193  const child& operator [] (size_type index) const noexcept {
3194  return children_[index];
3195  }
3196 
3197  //---------------------------------------------------------------
3199  child& front() noexcept { return children_.front(); }
3201  const child& front() const noexcept { return children_.front(); }
3202  //-----------------------------------------------------
3204  child& back() noexcept { return children_.back(); }
3206  const child& back() const noexcept { return children_.back(); }
3208 
3209  //---------------------------------------------------------------
3211  bool empty() const noexcept { return children_.empty(); }
3214  size_type size() const noexcept { return children_.size(); }
3215 
3217  size_type depth() const {
3218  size_type n = 0;
3219  for(const auto& c : children_) {
3220  auto l = 1 + c.depth();
3221  if(l > n) n = l;
3222  }
3223  return n;
3224  }
3226 
3227  //---------------------------------------------------------------
3229  iterator begin() noexcept { return children_.begin(); }
3231  const_iterator begin() const noexcept { return children_.begin(); }
3233  const_iterator cbegin() const noexcept { return children_.begin(); }
3234 
3236  iterator end() noexcept { return children_.end(); }
3238  const_iterator end() const noexcept { return children_.end(); }
3240  const_iterator cend() const noexcept { return children_.end(); }
3242 
3243  //---------------------------------------------------------------
3248  begin_dfs() const noexcept {
3249  return depth_first_traverser{*this};
3250  }
3251 
3252 
3253  //---------------------------------------------------------------
3255  size_type param_count() const {
3256  size_type c = 0;
3257  for(const auto& n : children_) {
3258  c += n.param_count();
3259  }
3260  return c;
3261  }
3262 
3264  //---------------------------------------------------------------
3266  arg_list all_flags() const
3267  {
3268  std::vector<arg_string> all;
3269  gather_flags(children_, all);
3270  return all;
3271  }
3272 
3275  bool flags_are_prefix_free() const
3276  {
3277  const auto fs = all_flags();
3278 
3279  using std::begin; using std::end;
3280  for(auto i = begin(fs), e = end(fs); i != e; ++i) {
3281  if(!i->empty()) {
3282  for(auto j = i+1; j != e; ++j) {
3283  if(!j->empty() && *i != *j) {
3284  if(i->find(*j) == 0) return false;
3285  if(j->find(*i) == 0) return false;
3286  }
3287  }
3288  }
3289  }
3290 
3291  return true;
3292  }
3293 
3294 
3295  //---------------------------------------------------------------
3298  {
3299  arg_list prefixes;
3300  gather_prefixes(children_, prefixes);
3301  return str::longest_common_prefix(prefixes);
3302  }
3303 
3304 
3305 private:
3306  //---------------------------------------------------------------
3307  static void
3308  gather_flags(const children_store& nodes, arg_list& all)
3309  {
3310  for(const auto& p : nodes) {
3311  if(p.is_group()) {
3312  gather_flags(p.as_group().children_, all);
3313  }
3314  else {
3315  const auto& pf = p.as_param().flags();
3316  using std::begin;
3317  using std::end;
3318  if(!pf.empty()) all.insert(end(all), begin(pf), end(pf));
3319  }
3320  }
3321  }
3322  //---------------------------------------------------------------
3323  static void
3324  gather_prefixes(const children_store& nodes, arg_list& all)
3325  {
3326  for(const auto& p : nodes) {
3327  if(p.is_group()) {
3328  gather_prefixes(p.as_group().children_, all);
3329  }
3330  else if(!p.as_param().flags().empty()) {
3331  auto pfx = str::longest_common_prefix(p.as_param().flags());
3332  if(!pfx.empty()) all.push_back(std::move(pfx));
3333  }
3334  }
3335  }
3336 
3337  //---------------------------------------------------------------
3338  children_store children_;
3339  bool exclusive_ = false;
3340  bool joinable_ = false;
3341  bool scoped_ = false;
3342 };
3343 
3344 
3345 
3346 /*************************************************************************/
3351 using pattern = group::child;
3352 
3353 
3354 
3355 /*************************************************************************/
3360 inline group
3361 operator , (parameter a, parameter b)
3362 {
3363  return group{std::move(a), std::move(b)}.scoped(false);
3364 }
3365 
3366 //---------------------------------------------------------
3367 inline group
3368 operator , (parameter a, group b)
3370  return !b.scoped() && !b.blocking() && !b.exclusive() && !b.repeatable()
3371  && !b.joinable() && (b.doc().empty() || b.doc() == a.doc())
3372  ? b.push_front(std::move(a))
3373  : group{std::move(a), std::move(b)}.scoped(false);
3374 }
3375 
3376 //---------------------------------------------------------
3377 inline group
3379 {
3380  return !a.scoped() && !a.blocking() && !a.exclusive() && !a.repeatable()
3381  && !a.joinable() && (a.doc().empty() || a.doc() == b.doc())
3382  ? a.push_back(std::move(b))
3383  : group{std::move(a), std::move(b)}.scoped(false);
3384 }
3385 
3386 //---------------------------------------------------------
3387 inline group
3388 operator , (group a, group b)
3389 {
3390  return !a.scoped() && !a.blocking() && !a.exclusive() && !a.repeatable()
3391  && !a.joinable() && (a.doc().empty() || a.doc() == b.doc())
3392  ? a.push_back(std::move(b))
3393  : group{std::move(a), std::move(b)}.scoped(false);
3394 }
3395 
3397 
3398 /*************************************************************************/
3403 template<class Param, class... Params>
3404 inline group
3405 one_of(Param param, Params... params)
3406 {
3407  return group{std::move(param), std::move(params)...}.exclusive(true);
3408 }
3409 
3410 
3411 /*************************************************************************/
3416 inline group
3417 operator | (parameter a, parameter b)
3418 {
3419  return group{std::move(a), std::move(b)}.scoped(false).exclusive(true);
3420 }
3421 
3422 //-------------------------------------------------------------------
3423 inline group
3424 operator | (parameter a, group b)
3426  return !b.scoped() && !b.blocking() && b.exclusive() && !b.repeatable()
3427  && !b.joinable()
3428  && (b.doc().empty() || b.doc() == a.doc())
3429  ? b.push_front(std::move(a))
3430  : group{std::move(a), std::move(b)}.scoped(false).exclusive(true);
3431 }
3433 //-------------------------------------------------------------------
3434 inline group
3436 {
3437  return !a.scoped() && a.exclusive() && !a.repeatable() && !a.joinable()
3438  && a.blocking() == b.blocking()
3439  && (a.doc().empty() || a.doc() == b.doc())
3440  ? a.push_back(std::move(b))
3441  : group{std::move(a), std::move(b)}.scoped(false).exclusive(true);
3442 }
3444 inline group
3445 operator | (group a, group b)
3446 {
3447  return !a.scoped() && a.exclusive() &&!a.repeatable() && !a.joinable()
3448  && a.blocking() == b.blocking()
3449  && (a.doc().empty() || a.doc() == b.doc())
3450  ? a.push_back(std::move(b))
3451  : group{std::move(a), std::move(b)}.scoped(false).exclusive(true);
3452 }
3454 
3455 
3456 namespace detail {
3457 
3458 inline void set_blocking(bool) {}
3459 
3460 template<class P, class... Ps>
3461 void set_blocking(bool yes, P& p, Ps&... ps) {
3462  p.blocking(yes);
3463  set_blocking(yes, ps...);
3464 }
3465 
3466 } // namespace detail
3467 
3468 
3469 /*************************************************************************/
3474 template<class Param, class... Params>
3475 inline group
3476 in_sequence(Param param, Params... params)
3477 {
3478  detail::set_blocking(true, param, params...);
3479  return group{std::move(param), std::move(params)...}.scoped(true);
3480 }
3481 
3482 
3483 /*************************************************************************/
3488 inline group
3490 {
3491  a.blocking(true);
3492  b.blocking(true);
3493  return group{std::move(a), std::move(b)}.scoped(true);
3494 }
3495 
3496 //---------------------------------------------------------
3497 inline group
3499 {
3500  a.blocking(true);
3501  return group{std::move(a), std::move(b)}.scoped(true);
3502 }
3503 
3504 //---------------------------------------------------------
3505 inline group
3507 {
3508  b.blocking(true);
3509  if(a.all_blocking() && !a.exclusive() && !a.repeatable() && !a.joinable()
3510  && (a.doc().empty() || a.doc() == b.doc()))
3511  {
3512  return a.push_back(std::move(b));
3513  }
3514  else {
3515  if(!a.all_blocking()) a.blocking(true);
3516  return group{std::move(a), std::move(b)}.scoped(true);
3517  }
3518 }
3519 
3520 inline group
3521 operator & (group a, group b)
3522 {
3523  if(!b.all_blocking()) b.blocking(true);
3524  if(a.all_blocking() && !a.exclusive() && !a.repeatable()
3525  && !a.joinable() && (a.doc().empty() || a.doc() == b.doc()))
3526  {
3527  return a.push_back(std::move(b));
3528  }
3529  else {
3530  if(!a.all_blocking()) a.blocking(true);
3531  return group{std::move(a), std::move(b)}.scoped(true);
3532  }
3533 }
3534 
3535 
3536 
3537 /*************************************************************************/
3543 inline group&
3544 joinable(group& param) {
3545  return param.joinable(true);
3546 }
3547 
3548 inline group&&
3549 joinable(group&& param) {
3550  return std::move(param.joinable(true));
3551 }
3553 //-------------------------------------------------------------------
3554 template<class... Params>
3555 inline group
3556 joinable(parameter param, Params... params)
3558  return group{std::move(param), std::move(params)...}.joinable(true);
3559 }
3560 
3561 template<class P2, class... Ps>
3562 inline group
3563 joinable(group p1, P2 p2, Ps... ps)
3565  return group{std::move(p1), std::move(p2), std::move(ps)...}.joinable(true);
3566 }
3567 
3568 template<class Param, class... Params>
3569 inline group
3570 joinable(doc_string docstr, Param param, Params... params)
3572  return group{std::move(param), std::move(params)...}
3573  .joinable(true).doc(std::move(docstr));
3574 }
3575 
3576 
3577 
3578 /*************************************************************************/
3583 inline parameter
3584 repeatable(parameter p) {
3585  return p.repeatable(true);
3586 }
3587 
3588 /*************************************************************************/
3593 inline group
3594 repeatable(group g) {
3595  return g.repeatable(true);
3596 }
3597 
3598 
3599 
3600 /*************************************************************************/
3609 template<class P2, class... Ps>
3610 inline group
3611 repeatable(parameter p1, P2 p2, Ps... ps)
3612 {
3613  return group{std::move(p1), std::move(p2),
3614  std::move(ps)...}.repeatable(true);
3615 }
3616 
3617 template<class P2, class... Ps>
3618 inline group
3619 repeatable(group p1, P2 p2, Ps... ps)
3620 {
3621  return group{std::move(p1), std::move(p2),
3622  std::move(ps)...}.repeatable(true);
3623 }
3624 
3625 
3626 
3627 /*************************************************************************/
3632 inline parameter&&
3633 with_prefix(const arg_string& prefix, parameter&& p) {
3634  return std::move(with_prefix(prefix, p));
3635 }
3636 
3637 
3638 //-------------------------------------------------------------------
3639 inline group&
3640 with_prefix(const arg_string& prefix, group& params)
3642  for(auto& p : params) {
3643  if(p.is_group()) {
3644  with_prefix(prefix, p.as_group());
3645  } else {
3646  with_prefix(prefix, p.as_param());
3647  }
3648  }
3649  return params;
3650 }
3651 
3652 
3653 inline group&&
3654 with_prefix(const arg_string& prefix, group&& params)
3655 {
3656  return std::move(with_prefix(prefix, params));
3657 }
3658 
3659 
3660 template<class Param, class... Params>
3661 inline group
3662 with_prefix(arg_string prefix, Param&& param, Params&&... params)
3663 {
3664  return with_prefix(prefix, group{std::forward<Param>(param),
3665  std::forward<Params>(params)...});
3666 }
3667 
3668 
3669 
3670 /*************************************************************************/
3678 inline parameter&&
3679 with_prefixes_short_long(const arg_string& shortpfx, const arg_string& longpfx,
3680  parameter&& p)
3681 {
3682  return std::move(with_prefixes_short_long(shortpfx, longpfx, p));
3683 }
3684 
3685 
3686 //-------------------------------------------------------------------
3687 inline group&
3688 with_prefixes_short_long(const arg_string& shortFlagPrefix,
3689  const arg_string& longFlagPrefix,
3690  group& params)
3691 {
3692  for(auto& p : params) {
3693  if(p.is_group()) {
3694  with_prefixes_short_long(shortFlagPrefix, longFlagPrefix, p.as_group());
3695  } else {
3696  with_prefixes_short_long(shortFlagPrefix, longFlagPrefix, p.as_param());
3697  }
3698  }
3699  return params;
3700 }
3701 
3702 
3703 inline group&&
3704 with_prefixes_short_long(const arg_string& shortFlagPrefix,
3705  const arg_string& longFlagPrefix,
3706  group&& params)
3707 {
3708  return std::move(with_prefixes_short_long(shortFlagPrefix, longFlagPrefix,
3709  params));
3710 }
3711 
3713 template<class Param, class... Params>
3714 inline group
3715 with_prefixes_short_long(const arg_string& shortFlagPrefix,
3716  const arg_string& longFlagPrefix,
3717  Param&& param, Params&&... params)
3718 {
3719  return with_prefixes_short_long(shortFlagPrefix, longFlagPrefix,
3720  group{std::forward<Param>(param),
3721  std::forward<Params>(params)...});
3722 }
3724 
3725 
3726 
3727 
3728 
3729 
3730 
3731 /*************************************************************************/
3736 namespace detail {
3737 
3738 
3739 /*************************************************************************/
3747 class scoped_dfs_traverser
3748 {
3749 public:
3751 
3752  scoped_dfs_traverser() = default;
3753 
3754  explicit
3756  pos_{g}, lastMatch_{}, posAfterLastMatch_{}, scopes_{},
3757  curMatched_{false}, ignoreBlocks_{false},
3758  repeatGroupStarted_{false}, repeatGroupContinues_{false}
3759  {}
3761  const dfs_traverser& base() const noexcept { return pos_; }
3762  const dfs_traverser& last_match() const noexcept { return lastMatch_; }
3764  const group& parent() const noexcept { return pos_.parent(); }
3765  const group* repeat_group() const noexcept { return pos_.repeat_group(); }
3766  const group* join_group() const noexcept { return pos_.join_group(); }
3767 
3768  const pattern* operator ->() const noexcept { return pos_.operator->(); }
3769  const pattern& operator *() const noexcept { return *pos_; }
3771  const pattern* ptr() const noexcept { return pos_.operator->(); }
3773  explicit operator bool() const noexcept { return bool(pos_); }
3775  bool joinable() const noexcept { return pos_.joinable(); }
3778  void ignore_blocking(bool yes) { ignoreBlocks_ = yes; }
3780  void invalidate() { pos_.invalidate(); curMatched_ = false; }
3781  bool matched() const noexcept { return curMatched_; }
3782 
3783  bool start_of_repeat_group() const noexcept { return repeatGroupStarted_; }
3785  //-----------------------------------------------------
3787  next_sibling() { pos_.next_sibling(); return *this; }
3790  next_alternative() { pos_.next_alternative(); return *this; }
3793  next_after_siblings() { pos_.next_after_siblings(); return *this; }
3794 
3795  //-----------------------------------------------------
3797  operator ++ ()
3798  {
3799  if(!pos_) return *this;
3800 
3801  if(pos_.is_last_in_path()) {
3802  return_to_outermost_scope();
3803  return *this;
3804  }
3806  //current pattern can block if it didn't match already
3807  if(!ignoreBlocks_ && !matched()) {
3808  //current group can block if we didn't have any match in it
3809  if(pos_.is_last_in_group() && pos_.parent().blocking()
3810  && (!posAfterLastMatch_ || &(posAfterLastMatch_.parent()) != &(pos_.parent())))
3811  {
3812  //ascend to parent's level
3813  ++pos_;
3814  //skip all siblings of parent group
3815  pos_.next_after_siblings();
3816  if(!pos_) return_to_outermost_scope();
3817  }
3818  else if(pos_->blocking() && !pos_->is_group()) {
3819  if(pos_.parent().exclusive()) { //is_alternative(pos_.level())) {
3820  pos_.next_alternative();
3821  } else {
3822  //no match => skip siblings of blocking param
3823  pos_.next_after_siblings();
3824  }
3825  if(!pos_) return_to_outermost_scope();
3826  } else {
3827  ++pos_;
3828  }
3829  } else {
3830  ++pos_;
3831  }
3832  check_left_scope();
3833  return *this;
3834  }
3835 
3836  //-----------------------------------------------------
3838  {
3839  if(!match || ignoreBlocks_) return;
3840 
3841  check_repeat_group_start(match);
3842 
3843  lastMatch_ = match.base();
3844 
3845  if(!match->blocking() && match.base().parent().blocking()) {
3846  match.pos_.back_to_parent();
3847  }
3848 
3849  //if match is not in current position & current position is blocking
3850  //=> current position has to be advanced by one so that it is
3851  //no longer reachable within current scope
3852  //(can happen for repeatable, blocking parameters)
3853  if(match.base() != pos_ && pos_->blocking()) pos_.next_sibling();
3854 
3855  if(match->blocking()) {
3856  if(match.pos_.is_alternative()) {
3857  //discard other alternatives
3858  match.pos_.skip_alternatives();
3859  }
3860 
3861  if(is_last_in_current_scope(match.pos_)) {
3862  //if current param is not repeatable -> back to previous scope
3863  if(!match->repeatable() && !match->is_group()) {
3864  curMatched_ = false;
3865  pos_ = std::move(match.pos_);
3866  if(!scopes_.empty()) pos_.undo(scopes_.top());
3867  }
3868  else { //stay at match position
3869  curMatched_ = true;
3870  pos_ = std::move(match.pos_);
3871  }
3872  }
3873  else { //not last in current group
3874  //if current param is not repeatable, go directly to next
3875  if(!match->repeatable() && !match->is_group()) {
3876  curMatched_ = false;
3877  ++match.pos_;
3878  } else {
3879  curMatched_ = true;
3880  }
3881 
3882  if(match.pos_.level() > pos_.level()) {
3883  scopes_.push(pos_.undo_point());
3884  pos_ = std::move(match.pos_);
3885  }
3886  else if(match.pos_.level() < pos_.level()) {
3887  return_to_level(match.pos_.level());
3888  }
3889  else {
3890  pos_ = std::move(match.pos_);
3891  }
3892  }
3893  posAfterLastMatch_ = pos_;
3894  }
3895  else {
3896  if(match.pos_.level() < pos_.level()) {
3897  return_to_level(match.pos_.level());
3898  }
3899  posAfterLastMatch_ = pos_;
3900  }
3901  repeatGroupContinues_ = repeat_group_continues();
3902  }
3903 
3904 private:
3905  //-----------------------------------------------------
3906  bool is_last_in_current_scope(const dfs_traverser& pos)
3907  {
3908  if(scopes_.empty()) return pos.is_last_in_path();
3909  //check if we would leave the current scope on ++
3910  auto p = pos;
3911  ++p;
3912  return p.level() < scopes_.top().level();
3913  }
3914 
3915  //-----------------------------------------------------
3916  void check_repeat_group_start(const scoped_dfs_traverser& newMatch)
3917  {
3918  const auto newrg = newMatch.repeat_group();
3919  if(!newrg) {
3920  repeatGroupStarted_ = false;
3921  }
3922  else if(lastMatch_.repeat_group() != newrg) {
3923  repeatGroupStarted_ = true;
3924  }
3925  else if(!repeatGroupContinues_ || !newMatch.repeatGroupContinues_) {
3926  repeatGroupStarted_ = true;
3927  }
3928  else {
3929  //special case: repeat group is outermost group
3930  //=> we can never really 'leave' and 'reenter' it
3931  //but if the current scope is the first element, then we are
3932  //conceptually at a position 'before' the group
3933  repeatGroupStarted_ = scopes_.empty() || (
3934  newrg == pos_.root() &&
3935  scopes_.top().param() == &(*pos_.root()->begin()) );
3936  }
3937  repeatGroupContinues_ = repeatGroupStarted_;
3938  }
3939 
3940  //-----------------------------------------------------
3941  bool repeat_group_continues()
3942  {
3943  if(!repeatGroupContinues_) return false;
3944  const auto curRepGroup = pos_.repeat_group();
3945  if(!curRepGroup) return false;
3946  if(curRepGroup != lastMatch_.repeat_group()) return false;
3947  if(!posAfterLastMatch_) return false;
3948  return true;
3949  }
3950 
3951  //-----------------------------------------------------
3952  void check_left_scope()
3953  {
3954  if(posAfterLastMatch_) {
3955  if(pos_.level() < posAfterLastMatch_.level()) {
3956  while(!scopes_.empty() && scopes_.top().level() >= pos_.level()) {
3957  pos_.undo(scopes_.top());
3958  scopes_.pop();
3959  }
3960  posAfterLastMatch_.invalidate();
3961  }
3962  }
3963  while(!scopes_.empty() && scopes_.top().level() > pos_.level()) {
3964  pos_.undo(scopes_.top());
3965  scopes_.pop();
3966  }
3967  repeatGroupContinues_ = repeat_group_continues();
3968  }
3969 
3970  //-----------------------------------------------------
3971  void return_to_outermost_scope()
3972  {
3973  posAfterLastMatch_.invalidate();
3974 
3975  if(scopes_.empty()) {
3976  pos_.invalidate();
3977  repeatGroupContinues_ = false;
3978  return;
3979  }
3980 
3981  while(!scopes_.empty() && (!pos_ || pos_.level() >= 1)) {
3982  pos_.undo(scopes_.top());
3983  scopes_.pop();
3984  }
3985  while(!scopes_.empty()) scopes_.pop();
3986 
3987  repeatGroupContinues_ = repeat_group_continues();
3988  }
3989 
3990  //-----------------------------------------------------
3991  void return_to_level(int level)
3992  {
3993  if(pos_.level() <= level) return;
3994  while(!scopes_.empty() && pos_.level() > level) {
3995  pos_.undo(scopes_.top());
3996  scopes_.pop();
3997  }
3998  };
3999 
4000  dfs_traverser pos_;
4001  dfs_traverser lastMatch_;
4002  dfs_traverser posAfterLastMatch_;
4003  std::stack<dfs_traverser::memento> scopes_;
4004  bool curMatched_ = false;
4005  bool ignoreBlocks_ = false;
4006  bool repeatGroupStarted_ = false;
4007  bool repeatGroupContinues_ = false;
4008 };
4009 
4010 
4011 
4012 
4013 /*****************************************************************************
4014  *
4015  * some parameter property predicates
4016  *
4017  *****************************************************************************/
4018 struct select_all {
4019  bool operator () (const parameter&) const noexcept { return true; }
4020 };
4021 
4022 struct select_flags {
4023  bool operator () (const parameter& p) const noexcept {
4024  return !p.flags().empty();
4025  }
4026 };
4027 
4028 struct select_values {
4029  bool operator () (const parameter& p) const noexcept {
4030  return p.flags().empty();
4031  }
4032 };
4033 
4034 
4035 
4036 /*************************************************************************/
4041 class match_t {
4042 public:
4043  match_t() = default;
4045  str_{std::move(s)}, pos_{std::move(p)}
4046  {}
4047 
4048  const arg_string& str() const noexcept { return str_; }
4049  const scoped_dfs_traverser& pos() const noexcept { return pos_; }
4050 
4051  explicit operator bool() const noexcept { return !str_.empty(); }
4053 private:
4054  arg_string str_;
4055  scoped_dfs_traverser pos_;
4056 };
4058 
4060 /*************************************************************************/
4066 template<class Predicate>
4067 match_t
4069  const Predicate& select)
4070 {
4071  if(arg.empty()) return match_t{};
4072 
4073  while(pos) {
4074  if(pos->is_param()) {
4075  const auto& param = pos->as_param();
4076  if(select(param)) {
4077  const auto match = param.match(arg);
4078  if(match && match.length() == arg.size()) {
4079  return match_t{arg, std::move(pos)};
4080  }
4081  }
4082  }
4083  ++pos;
4084  }
4085  return match_t{};
4086 }
4087 
4088 
4089 
4090 /*************************************************************************/
4097 template<class Predicate>
4098 match_t
4099 prefix_match(scoped_dfs_traverser pos, const arg_string& arg,
4100  const Predicate& select)
4101 {
4102  if(arg.empty()) return match_t{};
4103 
4104  while(pos) {
4105  if(pos->is_param()) {
4106  const auto& param = pos->as_param();
4107  if(select(param)) {
4108  const auto match = param.match(arg);
4109  if(match.prefix()) {
4110  if(match.length() == arg.size()) {
4111  return match_t{arg, std::move(pos)};
4112  }
4113  else {
4114  return match_t{arg.substr(match.at(), match.length()),
4115  std::move(pos)};
4116  }
4117  }
4118  }
4119  }
4120  ++pos;
4121  }
4122  return match_t{};
4123 }
4124 
4125 
4126 
4127 /*************************************************************************/
4133 template<class Predicate>
4134 match_t
4135 partial_match(scoped_dfs_traverser pos, const arg_string& arg,
4136  const Predicate& select)
4137 {
4138  if(arg.empty()) return match_t{};
4139 
4140  while(pos) {
4141  if(pos->is_param()) {
4142  const auto& param = pos->as_param();
4143  if(select(param)) {
4144  const auto match = param.match(arg);
4145  if(match) {
4146  return match_t{arg.substr(match.at(), match.length()),
4147  std::move(pos)};
4148  }
4149  }
4150  }
4151  ++pos;
4152  }
4153  return match_t{};
4154 }
4155 
4156 } //namespace detail
4157 
4158 
4159 
4160 
4161 
4162 
4163 /***************************************************************/
4168 class parser
4169 {
4170 public:
4171  using dfs_traverser = group::depth_first_traverser;
4172  using scoped_dfs_traverser = detail::scoped_dfs_traverser;
4173 
4174 
4175  /*****************************************************/
4178  class arg_mapping {
4179  public:
4180  friend class parser;
4181 
4182  explicit
4184  const dfs_traverser& match)
4185  :
4186  index_{idx}, arg_{std::move(s)}, match_{match},
4187  repeat_{0}, startsRepeatGroup_{false},
4188  blocked_{false}, conflict_{false}
4189  {}
4190 
4191  explicit
4192  arg_mapping(arg_index idx, arg_string s) :
4193  index_{idx}, arg_{std::move(s)}, match_{},
4194  repeat_{0}, startsRepeatGroup_{false},
4195  blocked_{false}, conflict_{false}
4196  {}
4197 
4198  arg_index index() const noexcept { return index_; }
4199  const arg_string& arg() const noexcept { return arg_; }
4201  const parameter* param() const noexcept {
4202  return match_ && match_->is_param()
4203  ? &(match_->as_param()) : nullptr;
4204  }
4205 
4206  std::size_t repeat() const noexcept { return repeat_; }
4208  bool blocked() const noexcept { return blocked_; }
4209  bool conflict() const noexcept { return conflict_; }
4210 
4211  bool bad_repeat() const noexcept {
4212  if(!param()) return false;
4213  return repeat_ > 0 && !param()->repeatable()
4214  && !match_.repeat_group();
4215  }
4217  bool any_error() const noexcept {
4218  return !match_ || blocked() || conflict() || bad_repeat();
4219  }
4220 
4221  private:
4222  arg_index index_;
4223  arg_string arg_;
4224  dfs_traverser match_;
4225  std::size_t repeat_;
4226  bool startsRepeatGroup_;
4227  bool blocked_;
4228  bool conflict_;
4229  };
4230 
4231  /*****************************************************/
4234  class missing_event {
4235  public:
4236  explicit
4237  missing_event(const parameter* p, arg_index after):
4238  param_{p}, aftIndex_{after}
4239  {}
4240 
4241  const parameter* param() const noexcept { return param_; }
4243  arg_index after_index() const noexcept { return aftIndex_; }
4244 
4245  private:
4246  const parameter* param_;
4247  arg_index aftIndex_;
4248  };
4250  //-----------------------------------------------------
4251  using missing_events = std::vector<missing_event>;
4252  using arg_mappings = std::vector<arg_mapping>;
4253 
4254 
4255 private:
4256  struct miss_candidate {
4257  miss_candidate(dfs_traverser p, arg_index idx,
4258  bool firstInRepeatGroup = false):
4259  pos{std::move(p)}, index{idx},
4260  startsRepeatGroup{firstInRepeatGroup}
4261  {}
4262 
4263  dfs_traverser pos;
4264  arg_index index;
4265  bool startsRepeatGroup;
4266  };
4267  using miss_candidates = std::vector<miss_candidate>;
4268 
4269 
4270 public:
4271  //---------------------------------------------------------------
4275  explicit
4276  parser(const group& root, arg_index offset = 0):
4277  root_{&root}, pos_{root},
4278  index_{offset-1}, eaten_{0},
4279  args_{}, missCand_{}, blocked_{false}
4280  {
4281  for_each_potential_miss(dfs_traverser{root},
4282  [this](const dfs_traverser& p){
4283  missCand_.emplace_back(p, index_);
4284  });
4285  }
4286 
4287 
4288  //---------------------------------------------------------------
4290  bool operator() (const arg_string& arg)
4291  {
4292  ++eaten_;
4293  ++index_;
4294 
4295  if(!valid() || arg.empty()) return false;
4296 
4297  if(!blocked_ && try_match(arg)) return true;
4299  if(try_match_blocked(arg)) return false;
4300 
4301  //skipping of blocking & required patterns is not allowed
4302  if(!blocked_ && !pos_.matched() && pos_->required() && pos_->blocking()) {
4303  blocked_ = true;
4304  return false;
4305  }
4306 
4307  add_nomatch(arg);
4308  return false;
4309  }
4310 
4311 
4312  //---------------------------------------------------------------
4314  const arg_mappings& args() const {
4315  return args_;
4316  }
4317 
4319  missing_events missed() const {
4320  missing_events misses;
4321  misses.reserve(missCand_.size());
4322  for(auto i = missCand_.begin(); i != missCand_.end(); ++i) {
4323  misses.emplace_back(&(i->pos->as_param()), i->index);
4324  }
4325  return misses;
4326  }
4329  arg_index parse_count() const noexcept { return eaten_; }
4330 
4334  bool valid() const noexcept { return bool(pos_); }
4335 
4339  explicit operator bool() const noexcept { return valid(); }
4340 
4341 
4342 private:
4343  //---------------------------------------------------------------
4344  using match_t = detail::match_t;
4345 
4346 
4347  //---------------------------------------------------------------
4349  bool try_match_blocked(const arg_string& arg)
4350  {
4351  //try to match ahead (using temporary parser)
4352  if(pos_) {
4353  auto ahead = *this;
4354  if(try_match_blocked(std::move(ahead), arg)) return true;
4355  }
4356 
4357  //try to match from the beginning (using temporary parser)
4358  if(root_) {
4359  parser all{*root_, index_+1};
4360  if(try_match_blocked(std::move(all), arg)) return true;
4361  }
4362 
4363  return false;
4364  }
4365 
4366  //---------------------------------------------------------------
4367  bool try_match_blocked(parser&& parse, const arg_string& arg)
4368  {
4369  const auto nold = int(parse.args_.size());
4370 
4371  parse.pos_.ignore_blocking(true);
4372 
4373  if(!parse.try_match(arg)) return false;
4374 
4375  for(auto i = parse.args_.begin() + nold; i != parse.args_.end(); ++i) {
4376  args_.push_back(*i);
4377  args_.back().blocked_ = true;
4378  }
4379  return true;
4380  }
4381 
4382  //---------------------------------------------------------------
4384  bool try_match(const arg_string& arg)
4385  {
4386  //Note: flag-params will always take precedence over value-params
4387  if(try_match_full(arg, detail::select_flags{})) return true;
4388  if(try_match_joined_flags(arg)) return true;
4389  if(try_match_joined_sequence(arg, detail::select_flags{})) return true;
4390  if(try_match_full(arg, detail::select_values{})) return true;
4391  if(try_match_joined_sequence(arg, detail::select_all{})) return true;
4392  if(try_match_joined_params(arg)) return true;
4393  return false;
4394  }
4395 
4396  //---------------------------------------------------------------
4397  template<class Predicate>
4398  bool try_match_full(const arg_string& arg, const Predicate& select)
4399  {
4400  auto match = detail::full_match(pos_, arg, select);
4401 
4402  if(!match) return false;
4403 
4404  add_match(match);
4405  return true;
4406  }
4407 
4408  //---------------------------------------------------------------
4409  template<class Predicate>
4410  bool try_match_joined_sequence(arg_string arg, const Predicate& acceptFirst)
4411  {
4412  auto fstMatch = detail::prefix_match(pos_, arg, acceptFirst);
4413 
4414  if(!fstMatch) return false;
4415 
4416  if(fstMatch.str().size() == arg.size()) {
4417  add_match(fstMatch);
4418  return true;
4419  }
4420 
4421  if(!fstMatch.pos()->blocking()) return false;
4422 
4423  auto pos = fstMatch.pos();
4424  pos.ignore_blocking(true);
4425  const auto parent = &pos.parent();
4426  if(!pos->repeatable()) ++pos;
4427 
4428  arg.erase(0, fstMatch.str().size());
4429  std::vector<match_t> matches { std::move(fstMatch) };
4430 
4431  while(!arg.empty() && pos &&
4432  pos->blocking() && pos->is_param() &&
4433  (&pos.parent() == parent))
4434  {
4435  auto match = pos->as_param().match(arg);
4436 
4437  if(match.prefix()) {
4438  matches.emplace_back(arg.substr(0,match.length()), pos);
4439  arg.erase(0, match.length());
4440  if(!pos->repeatable()) ++pos;
4441  }
4442  else {
4443  if(!pos->repeatable()) return false;
4444  ++pos;
4445  }
4446 
4447  }
4448 
4449  if(!arg.empty() || matches.empty()) return false;
4450 
4451  for(const auto& m : matches) add_match(m);
4452  return true;
4453  }
4454 
4455  //-----------------------------------------------------
4456  bool try_match_joined_flags(const arg_string& arg)
4457  {
4458  return try_match_joined([&](const group& g) {
4459  if(try_match_joined(g, arg, detail::select_flags{},
4460  g.common_flag_prefix()) )
4461  {
4462  return true;
4463  }
4464  return false;
4465  });
4466  }
4467 
4468  //---------------------------------------------------------------
4469  bool try_match_joined_params(const arg_string& arg)
4470  {
4471  return try_match_joined([&](const group& g) {
4472  if(try_match_joined(g, arg, detail::select_all{}) ) {
4473  return true;
4474  }
4475  return false;
4476  });
4477  }
4478 
4479  //-----------------------------------------------------
4480  template<class Predicate>
4481  bool try_match_joined(const group& joinGroup, arg_string arg,
4482  const Predicate& pred,
4483  const arg_string& prefix = "")
4484  {
4485  parser parse {joinGroup};
4486  std::vector<match_t> matches;
4487 
4488  while(!arg.empty()) {
4489  auto match = detail::prefix_match(parse.pos_, arg, pred);
4490 
4491  if(!match) return false;
4492 
4493  arg.erase(0, match.str().size());
4494  //make sure prefix is always present after the first match
4495  //ensures that, e.g., flags "-a" and "-b" will be found in "-ab"
4496  if(!arg.empty() && !prefix.empty() && arg.find(prefix) != 0 &&
4497  prefix != match.str())
4498  {
4499  arg.insert(0,prefix);
4500  }
4501 
4502  parse.add_match(match);
4503  matches.push_back(std::move(match));
4504  }
4505 
4506  if(!arg.empty() || matches.empty()) return false;
4507 
4508  if(!parse.missCand_.empty()) return false;
4509  for(const auto& a : parse.args_) if(a.any_error()) return false;
4510 
4511  //replay matches onto *this
4512  for(const auto& m : matches) add_match(m);
4513  return true;
4514  }
4515 
4516  //-----------------------------------------------------
4517  template<class Predicate>
4518  bool try_match_joined(const Predicate& pred)
4519  {
4520  if(pos_ && pos_.parent().joinable()) {
4521  const auto& g = pos_.parent();
4522  if(pred(g)) return true;
4523  return false;
4524  }
4525 
4526  auto pos = pos_;
4527  while(pos) {
4528  if(pos->is_group() && pos->as_group().joinable()) {
4529  const auto& g = pos->as_group();
4530  if(pred(g)) return true;
4531  pos.next_sibling();
4532  }
4533  else {
4534  ++pos;
4535  }
4536  }
4537  return false;
4538  }
4539 
4540 
4541  //---------------------------------------------------------------
4542  void add_nomatch(const arg_string& arg) {
4543  args_.emplace_back(index_, arg);
4544  }
4545 
4546 
4547  //---------------------------------------------------------------
4548  void add_match(const match_t& match)
4549  {
4550  const auto& pos = match.pos();
4551  if(!pos || !pos->is_param() || match.str().empty()) return;
4552 
4553  pos_.next_after_match(pos);
4554 
4555  arg_mapping newArg{index_, match.str(), pos.base()};
4556  newArg.repeat_ = occurrences_of(&pos->as_param());
4557  newArg.conflict_ = check_conflicts(pos.base());
4558  newArg.startsRepeatGroup_ = pos_.start_of_repeat_group();
4559  args_.push_back(std::move(newArg));
4560 
4561  add_miss_candidates_after(pos);
4562  clean_miss_candidates_for(pos.base());
4563  discard_alternative_miss_candidates(pos.base());
4564 
4565  }
4566 
4567  //-----------------------------------------------------
4568  bool check_conflicts(const dfs_traverser& match)
4569  {
4570  if(pos_.start_of_repeat_group()) return false;
4571  bool conflict = false;
4572  for(const auto& m : match.stack()) {
4573  if(m.parent->exclusive()) {
4574  for(auto i = args_.rbegin(); i != args_.rend(); ++i) {
4575  if(!i->blocked()) {
4576  for(const auto& c : i->match_.stack()) {
4577  //sibling within same exclusive group => conflict
4578  if(c.parent == m.parent && c.cur != m.cur) {
4579  conflict = true;
4580  i->conflict_ = true;
4581  }
4582  }
4583  }
4584  //check for conflicts only within current repeat cycle
4585  if(i->startsRepeatGroup_) break;
4586  }
4587  }
4588  }
4589  return conflict;
4590  }
4591 
4592  //-----------------------------------------------------
4593  void clean_miss_candidates_for(const dfs_traverser& match)
4594  {
4595  auto i = std::find_if(missCand_.rbegin(), missCand_.rend(),
4596  [&](const miss_candidate& m) {
4597  return &(*m.pos) == &(*match);
4598  });
4599 
4600  if(i != missCand_.rend()) {
4601  missCand_.erase(prev(i.base()));
4602  }
4603  }
4604 
4605  //-----------------------------------------------------
4606  void discard_alternative_miss_candidates(const dfs_traverser& match)
4607  {
4608  if(missCand_.empty()) return;
4609  //find out, if miss candidate is sibling of one of the same
4610  //alternative groups that the current match is a member of
4611  //if so, we can discard the miss
4612 
4613  //go through all exclusive groups of matching pattern
4614  for(const auto& m : match.stack()) {
4615  if(m.parent->exclusive()) {
4616  for(auto i = int(missCand_.size())-1; i >= 0; --i) {
4617  bool removed = false;
4618  for(const auto& c : missCand_[i].pos.stack()) {
4619  //sibling within same exclusive group => discard
4620  if(c.parent == m.parent && c.cur != m.cur) {
4621  missCand_.erase(missCand_.begin() + i);
4622  if(missCand_.empty()) return;
4623  removed = true;
4624  break;
4625  }
4626  }
4627  //remove miss candidates only within current repeat cycle
4628  if(i > 0 && removed) {
4629  if(missCand_[i-1].startsRepeatGroup) break;
4630  } else {
4631  if(missCand_[i].startsRepeatGroup) break;
4632  }
4633  }
4634  }
4635  }
4636  }
4637 
4638  //-----------------------------------------------------
4639  void add_miss_candidates_after(const scoped_dfs_traverser& match)
4640  {
4641  auto npos = match.base();
4642  if(npos.is_alternative()) npos.skip_alternatives();
4643  ++npos;
4644  //need to add potential misses if:
4645  //either new repeat group was started
4646  const auto newRepGroup = match.repeat_group();
4647  if(newRepGroup) {
4648  if(pos_.start_of_repeat_group()) {
4649  for_each_potential_miss(std::move(npos),
4650  [&,this](const dfs_traverser& pos) {
4651  //only add candidates within repeat group
4652  if(newRepGroup == pos.repeat_group()) {
4653  missCand_.emplace_back(pos, index_, true);
4654  }
4655  });
4656  }
4657  }
4658  //... or an optional blocking param was hit
4659  else if(match->blocking() && !match->required() &&
4660  npos.level() >= match.base().level())
4661  {
4662  for_each_potential_miss(std::move(npos),
4663  [&,this](const dfs_traverser& pos) {
4664  //only add new candidates
4665  if(std::find_if(missCand_.begin(), missCand_.end(),
4666  [&](const miss_candidate& c){
4667  return &(*c.pos) == &(*pos);
4668  }) == missCand_.end())
4669  {
4670  missCand_.emplace_back(pos, index_);
4671  }
4672  });
4673  }
4674 
4675  }
4676 
4677  //-----------------------------------------------------
4678  template<class Action>
4679  static void
4680  for_each_potential_miss(dfs_traverser pos, Action&& action)
4681  {
4682  const auto level = pos.level();
4683  while(pos && pos.level() >= level) {
4684  if(pos->is_group() ) {
4685  const auto& g = pos->as_group();
4686  if(g.all_optional() || (g.exclusive() && g.any_optional())) {
4687  pos.next_sibling();
4688  } else {
4689  ++pos;
4690  }
4691  } else { //param
4692  if(pos->required()) {
4693  action(pos);
4694  ++pos;
4695  } else if(pos->blocking()) { //optional + blocking
4696  pos.next_after_siblings();
4697  } else {
4698  ++pos;
4699  }
4700  }
4701  }
4702  }
4703 
4704 
4705  //---------------------------------------------------------------
4706  std::size_t occurrences_of(const parameter* p) const
4707  {
4708  auto i = std::find_if(args_.rbegin(), args_.rend(),
4709  [p](const arg_mapping& a){ return a.param() == p; });
4710 
4711  if(i != args_.rend()) return i->repeat() + 1;
4712  return 0;
4713  }
4714 
4715 
4716  //---------------------------------------------------------------
4717  const group* root_;
4718  scoped_dfs_traverser pos_;
4719  arg_index index_;
4720  arg_index eaten_;
4721  arg_mappings args_;
4722  miss_candidates missCand_;
4723  bool blocked_;
4724 };
4725 
4726 
4727 
4728 
4729 /*************************************************************************/
4735 class parsing_result
4736 {
4737 public:
4738  using arg_mapping = parser::arg_mapping;
4740  using missing_event = parser::missing_event;
4742  using iterator = arg_mappings::const_iterator;
4744  //-----------------------------------------------------
4746  parsing_result() = default;
4749  arg2param_{std::move(arg2param)}, missing_{std::move(misses)}
4750  {}
4751 
4752  //-----------------------------------------------------
4756  arg_mappings::size_type
4757  unmapped_args_count() const noexcept {
4758  return std::count_if(arg2param_.begin(), arg2param_.end(),
4759  [](const arg_mapping& a){ return !a.param(); });
4760  }
4761 
4765  bool any_blocked() const noexcept {
4766  return std::any_of(arg2param_.begin(), arg2param_.end(),
4767  [](const arg_mapping& a){ return a.blocked(); });
4768  }
4769 
4772  bool any_conflict() const noexcept {
4773  return std::any_of(arg2param_.begin(), arg2param_.end(),
4774  [](const arg_mapping& a){ return a.conflict(); });
4775  }
4776 
4779  bool any_bad_repeat() const noexcept {
4780  return std::any_of(arg2param_.begin(), arg2param_.end(),
4781  [](const arg_mapping& a){ return a.bad_repeat(); });
4782  }
4783 
4786  bool any_error() const noexcept {
4787  return unmapped_args_count() > 0 || !missing().empty() ||
4789  }
4790 
4793  explicit operator bool() const noexcept { return !any_error(); }
4796  const missing_events& missing() const noexcept { return missing_; }
4797 
4800  iterator begin() const noexcept { return arg2param_.begin(); }
4803  iterator end() const noexcept { return arg2param_.end(); }
4805 private:
4806  //-----------------------------------------------------
4807  arg_mappings arg2param_;
4808  missing_events missing_;
4809 };
4810 
4812 
4813 
4814 namespace detail {
4815 namespace {
4816 
4817 /*************************************************************************/
4824 void sanitize_args(arg_list& args)
4825 {
4826  //e.g. {"-o12", ".34"} -> {"-o", "12.34"}
4827 
4828  if(args.empty()) return;
4829 
4830  for(auto i = begin(args)+1; i != end(args); ++i) {
4831  if(i != begin(args) && i->size() > 1 &&
4832  i->find('.') == 0 && std::isdigit((*i)[1]) )
4833  {
4834  //find trailing digits in previous arg
4835  using std::prev;
4836  auto& prv = *prev(i);
4837  auto fstDigit = std::find_if_not(prv.rbegin(), prv.rend(),
4838  [](arg_string::value_type c){
4839  return std::isdigit(c);
4840  }).base();
4841 
4842  //handle leading sign
4843  if(fstDigit > prv.begin() &&
4844  (*prev(fstDigit) == '+' || *prev(fstDigit) == '-'))
4845  {
4846  --fstDigit;
4847  }
4848 
4849  //prepend digits from previous arg
4850  i->insert(begin(*i), fstDigit, end(prv));
4851 
4852  //erase digits in previous arg
4853  prv.erase(fstDigit, end(prv));
4854  }
4855  }
4856 }
4857 
4858 
4859 
4860 /*************************************************************************/
4865 void execute_actions(const parsing_result& res)
4866 {
4867  for(const auto& m : res) {
4868  if(m.param()) {
4869  const auto& param = *(m.param());
4870 
4871  if(m.repeat() > 0) param.notify_repeated(m.index());
4872  if(m.blocked()) param.notify_blocked(m.index());
4873  if(m.conflict()) param.notify_conflict(m.index());
4874  //main action
4875  if(!m.any_error()) param.execute_actions(m.arg());
4876  }
4877  }
4878 
4879  for(auto m : res.missing()) {
4880  if(m.param()) m.param()->notify_missing(m.after_index());
4881  }
4882 }
4883 
4884 
4885 
4886 /*************************************************************************/
4891 static parsing_result
4892 parse_args(const arg_list& args, const group& cli,
4893  arg_index offset = 0)
4894 {
4895  //parse args and store unrecognized arg indices
4896  parser parse{cli, offset};
4897  for(const auto& arg : args) {
4898  parse(arg);
4899  if(!parse.valid()) break;
4900  }
4901 
4902  return parsing_result{parse.args(), parse.missed()};
4903 }
4904 
4905 /*************************************************************************/
4910 static parsing_result
4911 parse_and_execute(const arg_list& args, const group& cli,
4912  arg_index offset = 0)
4913 {
4914  auto result = parse_args(args, cli, offset);
4915 
4916  execute_actions(result);
4917 
4918  return result;
4919 }
4920 
4921 } //anonymous namespace
4922 } // namespace detail
4923 
4924 
4925 
4926 
4927 /*************************************************************************/
4932 inline parsing_result
4933 parse(arg_list args, const group& cli)
4934 {
4935  detail::sanitize_args(args);
4936  return detail::parse_and_execute(args, cli);
4937 }
4938 
4939 
4940 /*************************************************************************/
4945 inline parsing_result
4946 parse(std::initializer_list<const char*> arglist, const group& cli)
4947 {
4948  arg_list args;
4949  args.reserve(arglist.size());
4950  for(auto a : arglist) {
4951  if(std::strlen(a) > 0) args.push_back(a);
4952  }
4953 
4954  return parse(std::move(args), cli);
4955 }
4956 
4957 
4958 /*************************************************************************/
4963 template<class InputIterator>
4964 inline parsing_result
4965 parse(InputIterator first, InputIterator last, const group& cli)
4966 {
4967  return parse(arg_list(first,last), cli);
4968 }
4969 
4970 
4971 /*************************************************************************/
4976 inline parsing_result
4977 parse(const int argc, char* argv[], const group& cli, arg_index offset = 1)
4978 {
4979  arg_list args;
4980  if(offset < argc) args.assign(argv+offset, argv+argc);
4981  detail::sanitize_args(args);
4982  return detail::parse_and_execute(args, cli, offset);
4983 }
4984 
4986 
4987 
4988 
4989 
4990 /*************************************************************************/
4996 class param_filter
4997 {
4998 public:
5000  param_filter& prefix(const arg_string& p) noexcept {
5001  prefix_ = p; return *this;
5002  }
5004  param_filter& prefix(arg_string&& p) noexcept {
5005  prefix_ = std::move(p); return *this;
5006  }
5007  const arg_string& prefix() const noexcept { return prefix_; }
5010  param_filter& required(tri t) noexcept { required_ = t; return *this; }
5011  tri required() const noexcept { return required_; }
5014  param_filter& blocking(tri t) noexcept { blocking_ = t; return *this; }
5015  tri blocking() const noexcept { return blocking_; }
5016 
5018  param_filter& repeatable(tri t) noexcept { repeatable_ = t; return *this; }
5019  tri repeatable() const noexcept { return repeatable_; }
5020 
5022  param_filter& has_doc(tri t) noexcept { hasDoc_ = t; return *this; }
5023  tri has_doc() const noexcept { return hasDoc_; }
5024 
5025 
5027  bool operator() (const parameter& p) const noexcept {
5028  if(!prefix_.empty()) {
5029  if(!std::any_of(p.flags().begin(), p.flags().end(),
5030  [&](const arg_string& flag){
5031  return str::has_prefix(flag, prefix_);
5032  })) return false;
5033  }
5034  if(required() != p.required()) return false;
5035  if(blocking() != p.blocking()) return false;
5036  if(repeatable() != p.repeatable()) return false;
5037  if(has_doc() != !p.doc().empty()) return false;
5038  return true;
5039  }
5040 
5041 private:
5042  arg_string prefix_;
5043  tri required_ = tri::either;
5044  tri blocking_ = tri::either;
5045  tri repeatable_ = tri::either;
5046  tri exclusive_ = tri::either;
5047  tri hasDoc_ = tri::yes;
5048 };
5049 
5050 
5051 
5052 
5053 
5054 
5055 /*************************************************************************/
5060 class doc_formatting
5061 {
5062 public:
5063  using string = doc_string;
5064 
5066  doc_formatting& start_column(int col) { startCol_ = col; return *this; }
5067  int start_column() const noexcept { return startCol_; }
5070  doc_formatting& doc_column(int col) { docCol_ = col; return *this; }
5071  int doc_column() const noexcept { return docCol_; }
5072 
5075  doc_formatting& indent_size(int indent) { indentSize_ = indent; return *this; }
5076  int indent_size() const noexcept { return indentSize_; }
5077 
5080  doc_formatting& empty_label(const string& label) {
5081  emptyLabel_ = label;
5082  return *this;
5083  }
5084  const string& empty_label() const noexcept { return emptyLabel_; }
5085 
5087  doc_formatting& param_separator(const string& sep) {
5088  paramSep_ = sep;
5089  return *this;
5090  }
5091  const string& param_separator() const noexcept { return paramSep_; }
5094  doc_formatting& group_separator(const string& sep) {
5095  groupSep_ = sep;
5096  return *this;
5097  }
5098  const string& group_separator() const noexcept { return groupSep_; }
5101  doc_formatting& alternative_param_separator(const string& sep) {
5102  altParamSep_ = sep;
5103  return *this;
5104  }
5105  const string& alternative_param_separator() const noexcept { return altParamSep_; }
5108  doc_formatting& alternative_group_separator(const string& sep) {
5109  altGroupSep_ = sep;
5110  return *this;
5111  }
5112  const string& alternative_group_separator() const noexcept { return altGroupSep_; }
5115  doc_formatting& flag_separator(const string& sep) {
5116  flagSep_ = sep;
5117  return *this;
5118  }
5119  const string& flag_separator() const noexcept { return flagSep_; }
5123  surround_labels(const string& prefix, const string& postfix) {
5124  labelPre_ = prefix;
5125  labelPst_ = postfix;
5126  return *this;
5127  }
5128  const string& label_prefix() const noexcept { return labelPre_; }
5129  const string& label_postfix() const noexcept { return labelPst_; }
5130 
5132  doc_formatting&
5133  surround_optional(const string& prefix, const string& postfix) {
5134  optionPre_ = prefix;
5135  optionPst_ = postfix;
5136  return *this;
5137  }
5138  const string& optional_prefix() const noexcept { return optionPre_; }
5139  const string& optional_postfix() const noexcept { return optionPst_; }
5140 
5142  doc_formatting&
5143  surround_repeat(const string& prefix, const string& postfix) {
5144  repeatPre_ = prefix;
5145  repeatPst_ = postfix;
5146  return *this;
5147  }
5148  const string& repeat_prefix() const noexcept { return repeatPre_; }
5149  const string& repeat_postfix() const noexcept { return repeatPst_; }
5150 
5152  doc_formatting&
5153  surround_alternatives(const string& prefix, const string& postfix) {
5154  alternPre_ = prefix;
5155  alternPst_ = postfix;
5156  return *this;
5157  }
5158  const string& alternatives_prefix() const noexcept { return alternPre_; }
5159  const string& alternatives_postfix() const noexcept { return alternPst_; }
5160 
5162  doc_formatting&
5163  surround_alternative_flags(const string& prefix, const string& postfix) {
5164  alternFlagPre_ = prefix;
5165  alternFlagPst_ = postfix;
5166  return *this;
5167  }
5168  const string& alternative_flags_prefix() const noexcept { return alternFlagPre_; }
5169  const string& alternative_flags_postfix() const noexcept { return alternFlagPst_; }
5170 
5172  doc_formatting&
5173  surround_group(const string& prefix, const string& postfix) {
5174  groupPre_ = prefix;
5175  groupPst_ = postfix;
5176  return *this;
5177  }
5178  const string& group_prefix() const noexcept { return groupPre_; }
5179  const string& group_postfix() const noexcept { return groupPst_; }
5180 
5182  doc_formatting&
5183  surround_joinable(const string& prefix, const string& postfix) {
5184  joinablePre_ = prefix;
5185  joinablePst_ = postfix;
5186  return *this;
5187  }
5188  const string& joinable_prefix() const noexcept { return joinablePre_; }
5189  const string& joinable_postfix() const noexcept { return joinablePst_; }
5190 
5193  doc_formatting& max_flags_per_param_in_doc(int max) {
5194  maxAltInDocs_ = max > 0 ? max : 0;
5195  return *this;
5196  }
5197  int max_flags_per_param_in_doc() const noexcept { return maxAltInDocs_; }
5198 
5202  maxAltInUsage_ = max > 0 ? max : 0;
5203  return *this;
5204  }
5205  int max_flags_per_param_in_usage() const noexcept { return maxAltInUsage_; }
5206 
5210  lineSpc_ = lines > 0 ? lines : 0;
5211  return *this;
5212  }
5213  int line_spacing() const noexcept { return lineSpc_; }
5214 
5218  doc_formatting& paragraph_spacing(int lines) {
5219  paragraphSpc_ = lines > 0 ? lines : 0;
5220  return *this;
5221  }
5222  int paragraph_spacing() const noexcept { return paragraphSpc_; }
5223 
5227  mergeAltCommonPfx_ = yes;
5228  return *this;
5229  }
5231  return mergeAltCommonPfx_;
5232  }
5233 
5237  mergeJoinableCommonPfx_ = yes;
5238  return *this;
5239  }
5240  bool merge_joinable_with_common_prefix() const noexcept {
5241  return mergeJoinableCommonPfx_;
5242  }
5243 
5247  doc_formatting& split_alternatives(bool yes = true) {
5248  splitTopAlt_ = yes;
5249  return *this;
5250  }
5251  bool split_alternatives() const noexcept {
5252  return splitTopAlt_;
5253  }
5254 
5257  doc_formatting& alternatives_min_split_size(int size) {
5258  groupSplitSize_ = size > 0 ? size : 0;
5259  return *this;
5260  }
5261  int alternatives_min_split_size() const noexcept { return groupSplitSize_; }
5262 
5263 private:
5264  string paramSep_ = string(" ");
5265  string groupSep_ = string(" ");
5266  string altParamSep_ = string("|");
5267  string altGroupSep_ = string(" | ");
5268  string flagSep_ = string(", ");
5269  string labelPre_ = string("<");
5270  string labelPst_ = string(">");
5271  string optionPre_ = string("[");
5272  string optionPst_ = string("]");
5273  string repeatPre_ = string("");
5274  string repeatPst_ = string("...");
5275  string groupPre_ = string("(");
5276  string groupPst_ = string(")");
5277  string alternPre_ = string("(");
5278  string alternPst_ = string(")");
5279  string alternFlagPre_ = string("");
5280  string alternFlagPst_ = string("");
5281  string joinablePre_ = string("(");
5282  string joinablePst_ = string(")");
5283  string emptyLabel_ = string("");
5284  int startCol_ = 8;
5285  int docCol_ = 20;
5286  int indentSize_ = 4;
5287  int maxAltInUsage_ = 1;
5288  int maxAltInDocs_ = 32;
5289  int lineSpc_ = 0;
5290  int paragraphSpc_ = 1;
5291  int groupSplitSize_ = 3;
5292  bool splitTopAlt_ = true;
5293  bool mergeAltCommonPfx_ = false;
5294  bool mergeJoinableCommonPfx_ = true;
5295 };
5296 
5297 
5298 
5299 
5300 /*************************************************************************/
5307 class usage_lines
5308 {
5309 public:
5310  using string = doc_string;
5311 
5312  usage_lines(const group& params, string prefix = "",
5313  const doc_formatting& fmt = doc_formatting{})
5314  :
5315  params_(params), fmt_(fmt), prefix_(std::move(prefix))
5316  {
5317  if(!prefix_.empty()) prefix_ += ' ';
5318  if(fmt_.start_column() > 0) prefix_.insert(0, fmt.start_column(), ' ');
5319  }
5321  usage_lines(const group& params, const doc_formatting& fmt):
5322  usage_lines(params, "", fmt)
5323  {}
5324 
5326  ommitOutermostSurrounders_ = yes;
5327  return *this;
5328  }
5330  return ommitOutermostSurrounders_;
5331  }
5332 
5333  template<class OStream>
5334  inline friend OStream& operator << (OStream& os, const usage_lines& p) {
5335  p.print_usage(os);
5336  return os;
5337  }
5338 
5339  string str() const {
5340  std::ostringstream os; os << *this; return os.str();
5341  }
5343 
5344 private:
5345  const group& params_;
5346  doc_formatting fmt_;
5347  string prefix_;
5348  bool ommitOutermostSurrounders_ = false;
5349 
5350 
5351  //-----------------------------------------------------
5352  struct context {
5354  std::stack<string> separators;
5355  std::stack<string> postfixes;
5356  int level = 0;
5357  const group* outermost = nullptr;
5358  bool linestart = false;
5359  bool useOutermost = true;
5360  int line = 0;
5361 
5362  bool is_singleton() const noexcept {
5363  return linestart && pos.is_last_in_path();
5364  }
5365  bool is_alternative() const noexcept {
5366  return pos.parent().exclusive();
5367  }
5368  };
5369 
5370 
5371  /***************************************************************/
5376  template<class OStream>
5377  void print_usage(OStream& os) const
5378  {
5379  context cur;
5380  cur.pos = params_.begin_dfs();
5381  cur.linestart = true;
5382  cur.level = cur.pos.level();
5383  cur.outermost = &params_;
5384 
5385  print_usage(os, cur, prefix_);
5386  }
5387 
5388 
5389  /***************************************************************/
5396  template<class OStream>
5397  void print_usage(OStream& os, context cur, string prefix) const
5398  {
5399  if(!cur.pos) return;
5400 
5401  std::ostringstream buf;
5402  if(cur.linestart) buf << prefix;
5403  const auto initPos = buf.tellp();
5404 
5405  cur.level = cur.pos.level();
5406 
5407  if(cur.useOutermost) {
5408  //we cannot start outside of the outermost group
5409  //so we have to treat it separately
5410  start_group(buf, cur.pos.parent(), cur);
5411  if(!cur.pos) {
5412  os << buf.str();
5413  return;
5414  }
5415  }
5416  else {
5417  //don't visit siblings of starter node
5418  cur.pos.skip_siblings();
5419  }
5420  check_end_group(buf, cur);
5421 
5422  do {
5423  if(buf.tellp() > initPos) cur.linestart = false;
5424  if(!cur.linestart && !cur.pos.is_first_in_group()) {
5425  buf << cur.separators.top();
5426  }
5427  if(cur.pos->is_group()) {
5428  start_group(buf, cur.pos->as_group(), cur);
5429  if(!cur.pos) {
5430  os << buf.str();
5431  return;
5432  }
5433  }
5434  else {
5435  buf << param_label(cur.pos->as_param(), cur);
5436  ++cur.pos;
5437  }
5438  check_end_group(buf, cur);
5439  } while(cur.pos);
5440 
5441  os << buf.str();
5442  }
5443 
5444 
5445  /***************************************************************/
5451  void start_group(std::ostringstream& os,
5452  const group& group, context& cur) const
5453  {
5454  //does cur.pos already point to a member or to group itself?
5455  //needed for special treatment of outermost group
5456  const bool alreadyInside = &(cur.pos.parent()) == &group;
5457 
5458  auto lbl = joined_label(group, cur);
5459  if(!lbl.empty()) {
5460  os << lbl;
5461  cur.linestart = false;
5462  //skip over entire group as its label has already been created
5463  if(alreadyInside) {
5464  cur.pos.next_after_siblings();
5465  } else {
5466  cur.pos.next_sibling();
5467  }
5468  }
5469  else {
5470  const bool splitAlternatives = group.exclusive() &&
5471  fmt_.split_alternatives() &&
5472  std::any_of(group.begin(), group.end(),
5473  [this](const pattern& p) {
5474  return int(p.param_count()) >= fmt_.alternatives_min_split_size();
5475  });
5476 
5477  if(splitAlternatives) {
5478  cur.postfixes.push("");
5479  cur.separators.push("");
5480  //recursively print alternative paths in decision-DAG
5481  //enter group?
5482  if(!alreadyInside) ++cur.pos;
5483  cur.linestart = true;
5484  cur.useOutermost = false;
5485  auto pfx = os.str();
5486  os.str("");
5487  //print paths in DAG starting at each group member
5488  for(std::size_t i = 0; i < group.size(); ++i) {
5489  std::stringstream buf;
5490  cur.outermost = cur.pos->is_group() ? &(cur.pos->as_group()) : nullptr;
5491  print_usage(buf, cur, pfx);
5492  if(buf.tellp() > int(pfx.size())) {
5493  os << buf.str();
5494  if(i < group.size()-1) {
5495  if(cur.line > 0) {
5496  os << string(fmt_.line_spacing(), '\n');
5497  }
5498  ++cur.line;
5499  os << '\n';
5500  }
5501  }
5502  cur.pos.next_sibling(); //do not descend into memebers
5503  }
5504  cur.pos.invalidate(); //signal end-of-path
5505  return;
5506  }
5507  else {
5508  //pre & postfixes, separators
5509  auto surround = group_surrounders(group, cur);
5510  os << surround.first;
5511  cur.postfixes.push(std::move(surround.second));
5512  cur.separators.push(group_separator(group, fmt_));
5513  //descend into group?
5514  if(!alreadyInside) ++cur.pos;
5515  }
5516  }
5517  cur.level = cur.pos.level();
5518  }
5519 
5520 
5521  /***************************************************************/
5524  void check_end_group(std::ostringstream& os, context& cur) const
5525  {
5526  for(; cur.level > cur.pos.level(); --cur.level) {
5527  os << cur.postfixes.top();
5528  cur.postfixes.pop();
5529  cur.separators.pop();
5530  }
5531  cur.level = cur.pos.level();
5532  }
5533 
5534 
5535  /***************************************************************/
5540  string param_label(const parameter& p, const context& cur) const
5541  {
5542  const auto& parent = cur.pos.parent();
5543 
5544  const bool startsOptionalSequence =
5545  parent.size() > 1 && p.blocking() && cur.pos.is_first_in_group();
5546 
5547  const bool outermost =
5548  ommitOutermostSurrounders_ && cur.outermost == &parent;
5549 
5550  const bool showopt = !cur.is_alternative() && !p.required()
5551  && !startsOptionalSequence && !outermost;
5552 
5553  const bool showrep = p.repeatable() && !outermost;
5554 
5555  string lbl;
5556 
5557  if(showrep) lbl += fmt_.repeat_prefix();
5558  if(showopt) lbl += fmt_.optional_prefix();
5559 
5560  const auto& flags = p.flags();
5561  if(!flags.empty()) {
5562  const int n = std::min(fmt_.max_flags_per_param_in_usage(),
5563  int(flags.size()));
5564 
5565  const bool surrAlt = n > 1 && !showopt && !cur.is_singleton();
5566 
5567  if(surrAlt) lbl += fmt_.alternative_flags_prefix();
5568  bool sep = false;
5569  for(int i = 0; i < n; ++i) {
5570  if(sep) {
5571  if(cur.is_singleton())
5572  lbl += fmt_.alternative_group_separator();
5573  else
5574  lbl += fmt_.flag_separator();
5575  }
5576  lbl += flags[i];
5577  sep = true;
5578  }
5579  if(surrAlt) lbl += fmt_.alternative_flags_postfix();
5580  }
5581  else {
5582  if(!p.label().empty()) {
5583  lbl += fmt_.label_prefix()
5584  + p.label()
5585  + fmt_.label_postfix();
5586  } else if(!fmt_.empty_label().empty()) {
5587  lbl += fmt_.label_prefix()
5588  + fmt_.empty_label()
5589  + fmt_.label_postfix();
5590  } else {
5591  return "";
5592  }
5593  }
5594 
5595  if(showopt) lbl += fmt_.optional_postfix();
5596  if(showrep) lbl += fmt_.repeat_postfix();
5597 
5598  return lbl;
5599  }
5600 
5601 
5602  /***************************************************************/
5607  string joined_label(const group& params, const context& cur) const
5608  {
5609  if(!fmt_.merge_alternative_flags_with_common_prefix() &&
5610  !fmt_.merge_joinable_with_common_prefix()) return "";
5611 
5612  const bool flagsonly = std::all_of(params.begin(), params.end(),
5613  [](const pattern& p){
5614  return p.is_param() && !p.as_param().flags().empty();
5615  });
5616 
5617  if(!flagsonly) return "";
5618 
5619  const bool showOpt = params.all_optional() &&
5620  !(ommitOutermostSurrounders_ && cur.outermost == &params);
5621 
5622  auto pfx = params.common_flag_prefix();
5623  if(pfx.empty()) return "";
5624 
5625  const auto n = pfx.size();
5626  if(params.exclusive() &&
5627  fmt_.merge_alternative_flags_with_common_prefix())
5628  {
5629  string lbl;
5630  if(showOpt) lbl += fmt_.optional_prefix();
5631  lbl += pfx + fmt_.alternatives_prefix();
5632  bool first = true;
5633  for(const auto& p : params) {
5634  if(p.is_param()) {
5635  if(first)
5636  first = false;
5637  else
5638  lbl += fmt_.alternative_param_separator();
5639  lbl += p.as_param().flags().front().substr(n);
5640  }
5641  }
5642  lbl += fmt_.alternatives_postfix();
5643  if(showOpt) lbl += fmt_.optional_postfix();
5644  return lbl;
5645  }
5646  //no alternatives, but joinable flags
5647  else if(params.joinable() &&
5648  fmt_.merge_joinable_with_common_prefix())
5649  {
5650  const bool allSingleChar = std::all_of(params.begin(), params.end(),
5651  [&](const pattern& p){
5652  return p.is_param() &&
5653  p.as_param().flags().front().substr(n).size() == 1;
5654  });
5655 
5656  if(allSingleChar) {
5657  string lbl;
5658  if(showOpt) lbl += fmt_.optional_prefix();
5659  lbl += pfx;
5660  for(const auto& p : params) {
5661  if(p.is_param())
5662  lbl += p.as_param().flags().front().substr(n);
5663  }
5664  if(showOpt) lbl += fmt_.optional_postfix();
5665  return lbl;
5666  }
5667  }
5668 
5669  return "";
5670  }
5671 
5672 
5673  /***************************************************************/
5678  std::pair<string,string>
5679  group_surrounders(const group& group, const context& cur) const
5680  {
5681  string prefix;
5682  string postfix;
5683 
5684  const bool isOutermost = &group == cur.outermost;
5685  if(isOutermost && ommitOutermostSurrounders_)
5686  return {string{}, string{}};
5687 
5688  if(group.exclusive()) {
5689  if(group.all_optional()) {
5690  prefix = fmt_.optional_prefix();
5691  postfix = fmt_.optional_postfix();
5692  if(group.all_flagless()) {
5693  prefix += fmt_.label_prefix();
5694  postfix = fmt_.label_prefix() + postfix;
5695  }
5696  } else if(group.all_flagless()) {
5697  prefix = fmt_.label_prefix();
5698  postfix = fmt_.label_postfix();
5699  } else if(!cur.is_singleton() || !isOutermost) {
5700  prefix = fmt_.alternatives_prefix();
5701  postfix = fmt_.alternatives_postfix();
5702  }
5703  }
5704  else if(group.size() > 1 &&
5705  group.front().blocking() && !group.front().required())
5706  {
5707  prefix = fmt_.optional_prefix();
5708  postfix = fmt_.optional_postfix();
5709  }
5710  else if(group.size() > 1 && cur.is_alternative() &&
5711  &group != cur.outermost)
5712  {
5713  prefix = fmt_.group_prefix();
5714  postfix = fmt_.group_postfix();
5715  }
5716  else if(!group.exclusive() &&
5717  group.joinable() && !cur.linestart)
5718  {
5719  prefix = fmt_.joinable_prefix();
5720  postfix = fmt_.joinable_postfix();
5721  }
5722 
5723  if(group.repeatable()) {
5724  if(prefix.empty()) prefix = fmt_.group_prefix();
5725  prefix = fmt_.repeat_prefix() + prefix;
5726  if(postfix.empty()) postfix = fmt_.group_postfix();
5727  postfix += fmt_.repeat_postfix();
5728  }
5729 
5730  return {std::move(prefix), std::move(postfix)};
5731  }
5732 
5733 
5734  /***************************************************************/
5739  static string
5740  group_separator(const group& group, const doc_formatting& fmt)
5741  {
5742  const bool only1ParamPerMember = std::all_of(group.begin(), group.end(),
5743  [](const pattern& p) { return p.param_count() < 2; });
5744 
5745  if(only1ParamPerMember) {
5746  if(group.exclusive()) {
5747  return fmt.alternative_param_separator();
5748  } else {
5749  return fmt.param_separator();
5750  }
5751  }
5752  else { //there is at least one large group inside
5753  if(group.exclusive()) {
5754  return fmt.alternative_group_separator();
5755  } else {
5756  return fmt.group_separator();
5757  }
5758  }
5759  }
5760 };
5761 
5762 
5763 
5764 
5765 /*************************************************************************/
5772 class documentation
5773 {
5774 public:
5775  using string = doc_string;
5776 
5777  documentation(const group& cli,
5778  const doc_formatting& fmt = doc_formatting{},
5779  const param_filter& filter = param_filter{})
5780  :
5781  cli_(cli), fmt_{fmt}, usgFmt_{fmt}, filter_{filter}
5782  {
5783  //necessary, because we re-use "usage_lines" to generate
5784  //labels for documented groups
5786  usgFmt_.max_flags_per_param_in_doc());
5787  }
5788 
5789  documentation(const group& params,
5790  const param_filter& filter,
5791  const doc_formatting& fmt = doc_formatting{})
5792  :
5793  documentation(params, fmt, filter)
5794  {}
5795 
5796  template<class OStream>
5797  inline friend OStream& operator << (OStream& os, const documentation& p) {
5798  printed prn = printed::nothing;
5799  p.print_doc(os, p.cli_, prn);
5800  return os;
5801  }
5802 
5803  string str() const {
5804  std::ostringstream os; os << *this; return os.str();
5805  }
5806 
5807 
5808 private:
5809  using dfs_traverser = group::depth_first_traverser;
5810  enum class printed { nothing, line, paragraph };
5812  const group& cli_;
5813  doc_formatting fmt_;
5814  doc_formatting usgFmt_;
5815  param_filter filter_;
5816 
5817 
5818  /***************************************************************/
5823  template<class OStream>
5824  void print_doc(OStream& os, const group& params,
5825  printed& sofar,
5826  int indentLvl = 0) const
5827  {
5828  if(params.empty()) return;
5829 
5830  //if group itself doesn't have docstring
5831  if(params.doc().empty()) {
5832  for(const auto& p : params) {
5833  print_doc(os, p, sofar, indentLvl);
5834  }
5835  }
5836  else { //group itself does have docstring
5837  bool anyDocInside = std::any_of(params.begin(), params.end(),
5838  [](const pattern& p){ return !p.doc().empty(); });
5839 
5840  if(anyDocInside) { //group docstring as title, then child entries
5841  if(sofar != printed::nothing) {
5842  os << string(fmt_.paragraph_spacing() + 1, '\n');
5843  }
5844  auto indent = string(fmt_.start_column(), ' ');
5845  if(indentLvl > 0) indent += string(fmt_.indent_size() * indentLvl, ' ');
5846  os << indent << params.doc() << '\n';
5847  sofar = printed::nothing;
5848  for(const auto& p : params) {
5849  print_doc(os, p, sofar, indentLvl + 1);
5850  }
5851  sofar = printed::paragraph;
5852  }
5853  else { //group label first then group docstring
5854  auto lbl = usage_lines(params, usgFmt_)
5856 
5857  str::trim(lbl);
5858  print_entry(os, lbl, params.doc(), fmt_, sofar, indentLvl);
5859  }
5860  }
5861  }
5862 
5863 
5864  /***************************************************************/
5869  template<class OStream>
5870  void print_doc(OStream& os, const pattern& ptrn,
5871  printed& sofar, int indentLvl) const
5872  {
5873  if(ptrn.is_group()) {
5874  print_doc(os, ptrn.as_group(), sofar, indentLvl);
5875  }
5876  else {
5877  const auto& p = ptrn.as_param();
5878  if(!filter_(p)) return;
5879  print_entry(os, param_label(p, fmt_), p.doc(), fmt_, sofar, indentLvl);
5880  }
5881  }
5882 
5883 
5884  /*********************************************************************/
5889  template<class OStream>
5890  static void
5891  print_entry(OStream& os,
5892  const string& label, const string& docstr,
5893  const doc_formatting& fmt, printed& sofar, int indentLvl)
5894  {
5895  if(label.empty()) return;
5896 
5897  auto indent = string(fmt.start_column(), ' ');
5898  if(indentLvl > 0) indent += string(fmt.indent_size() * indentLvl, ' ');
5899 
5900  const auto len = int(indent.size() + label.size());
5901  const bool oneline = len < fmt.doc_column();
5902 
5903  if(oneline) {
5904  if(sofar == printed::line)
5905  os << string(fmt.line_spacing() + 1, '\n');
5906  else if(sofar == printed::paragraph)
5907  os << string(fmt.paragraph_spacing() + 1, '\n');
5908  }
5909  else if(sofar != printed::nothing) {
5910  os << string(fmt.paragraph_spacing() + 1, '\n');
5911  }
5912 
5913  sofar = oneline ? printed::line : printed::paragraph;
5914 
5915  os << indent << label;
5916 
5917  if(!docstr.empty()) {
5918  if(oneline) {
5919  os << string(fmt.doc_column() - len, ' ');
5920  } else {
5921  os << '\n' << string(fmt.doc_column(), ' ');
5922  }
5923  os << docstr;
5924  }
5925  }
5926 
5927 
5928  /*********************************************************************/
5933  static doc_string
5934  param_label(const parameter& param, const doc_formatting& fmt)
5935  {
5936  doc_string lbl;
5937 
5938  if(param.repeatable()) lbl += fmt.repeat_prefix();
5939 
5940  const auto& flags = param.flags();
5941  if(!flags.empty()) {
5942  lbl += flags[0];
5943  const int n = std::min(fmt.max_flags_per_param_in_doc(),
5944  int(flags.size()));
5945  for(int i = 1; i < n; ++i) {
5946  lbl += fmt.flag_separator() + flags[i];
5947  }
5948  }
5949  else if(!param.label().empty() || !fmt.empty_label().empty()) {
5950  lbl += fmt.label_prefix();
5951  if(!param.label().empty()) {
5952  lbl += param.label();
5953  } else {
5954  lbl += fmt.empty_label();
5955  }
5956  lbl += fmt.label_postfix();
5957  }
5958 
5959  if(param.repeatable()) lbl += fmt.repeat_postfix();
5960 
5961  return lbl;
5962  }
5963 
5964 };
5965 
5966 
5967 
5968 
5969 /*************************************************************************/
5974 class man_page
5975 {
5976 public:
5977  //---------------------------------------------------------------
5978  using string = doc_string;
5979 
5980  //---------------------------------------------------------------
5982  class section {
5983  public:
5984  using string = doc_string;
5985 
5986  section(string stitle, string scontent):
5987  title_{std::move(stitle)}, content_{std::move(scontent)}
5988  {}
5989 
5990  const string& title() const noexcept { return title_; }
5991  const string& content() const noexcept { return content_; }
5993  private:
5994  string title_;
5995  string content_;
5996  };
5997 
5998 private:
5999  using section_store = std::vector<section>;
6000 
6001 public:
6002  //---------------------------------------------------------------
6003  using value_type = section;
6004  using const_iterator = section_store::const_iterator;
6005  using size_type = section_store::size_type;
6006 
6007 
6008  //---------------------------------------------------------------
6009  man_page&
6010  append_section(string title, string content)
6011  {
6012  sections_.emplace_back(std::move(title), std::move(content));
6013  return *this;
6014  }
6015  //-----------------------------------------------------
6016  man_page&
6017  prepend_section(string title, string content)
6018  {
6019  sections_.emplace(sections_.begin(),
6020  std::move(title), std::move(content));
6021  return *this;
6022  }
6023 
6024 
6025  //---------------------------------------------------------------
6026  const section& operator [] (size_type index) const noexcept {
6027  return sections_[index];
6028  }
6029 
6030  //---------------------------------------------------------------
6031  size_type size() const noexcept { return sections_.size(); }
6032 
6033  bool empty() const noexcept { return sections_.empty(); }
6035 
6036  //---------------------------------------------------------------
6037  const_iterator begin() const noexcept { return sections_.begin(); }
6038  const_iterator end() const noexcept { return sections_.end(); }
6040 
6041  //---------------------------------------------------------------
6042  man_page& program_name(const string& n) {
6043  progName_ = n;
6044  return *this;
6045  }
6046  man_page& program_name(string&& n) {
6047  progName_ = std::move(n);
6048  return *this;
6049  }
6050  const string& program_name() const noexcept {
6051  return progName_;
6052  }
6053 
6055  //---------------------------------------------------------------
6056  man_page& section_row_spacing(int rows) {
6057  sectionSpc_ = rows > 0 ? rows : 0;
6058  return *this;
6059  }
6060  int section_row_spacing() const noexcept { return sectionSpc_; }
6061 
6062 
6063 private:
6064  int sectionSpc_ = 1;
6065  section_store sections_;
6066  string progName_;
6067 };
6069 
6070 
6071 /*************************************************************************/
6077 inline man_page
6078 make_man_page(const group& params,
6079  doc_string progname = "",
6080  const doc_formatting& fmt = doc_formatting{})
6081 {
6082  man_page man;
6083  man.append_section("SYNOPSIS", usage_lines(params,progname,fmt).str());
6084  man.append_section("OPTIONS", documentation(params,fmt).str());
6085  return man;
6087 
6088 
6089 
6090 /*************************************************************************/
6095 template<class OStream>
6096 OStream&
6097 operator << (OStream& os, const man_page& man)
6098 {
6099  bool first = true;
6100  const auto secSpc = doc_string(man.section_row_spacing() + 1, '\n');
6101  for(const auto& section : man) {
6102  if(!section.content().empty()) {
6103  if(first) first = false; else os << secSpc;
6104  if(!section.title().empty()) os << section.title() << '\n';
6105  os << section.content();
6106  }
6107  }
6108  os << '\n';
6109  return os;
6110 }
6111 
6112 
6113 
6114 
6115 
6116 /*************************************************************************/
6121 namespace debug {
6122 
6123 
6124 /*************************************************************************/
6130 {
6131  if(!p.flags().empty()) return p.flags().front();
6132  if(!p.label().empty()) return p.label();
6133  return doc_string{"<?>"};
6134 }
6135 
6136 inline doc_string doc_label(const group&)
6138  return "<group>";
6139 }
6140 
6141 inline doc_string doc_label(const pattern& p)
6142 {
6143  return p.is_group() ? doc_label(p.as_group()) : doc_label(p.as_param());
6145 
6146 
6147 /*************************************************************************/
6152 template<class OStream>
6153 void print(OStream& os, const parsing_result& result)
6154 {
6155  for(const auto& m : result) {
6156  os << "#" << m.index() << " " << m.arg() << " -> ";
6157  auto p = m.param();
6158  if(p) {
6159  os << doc_label(*p) << " \t";
6160  if(m.repeat() > 0) {
6161  os << (m.bad_repeat() ? "[bad repeat " : "[repeat ")
6162  << m.repeat() << "]";
6163  }
6164  if(m.blocked()) os << " [blocked]";
6165  if(m.conflict()) os << " [conflict]";
6166  os << '\n';
6167  }
6168  else {
6169  os << " [unmapped]\n";
6170  }
6171  }
6172 
6173  for(const auto& m : result.missing()) {
6174  auto p = m.param();
6175  if(p) {
6176  os << doc_label(*p) << " \t";
6177  os << " [missing after " << m.after_index() << "]\n";
6178  }
6179  }
6180 }
6181 
6182 
6183 /*************************************************************************/
6188 template<class OStream>
6189 void print(OStream& os, const parameter& p)
6190 {
6191  if(p.blocking()) os << '!';
6192  if(!p.required()) os << '[';
6193  os << doc_label(p);
6194  if(p.repeatable()) os << "...";
6195  if(!p.required()) os << "]";
6196 }
6198 
6199 //-------------------------------------------------------------------
6200 template<class OStream>
6201 void print(OStream& os, const group& g, int level = 0);
6202 
6203 
6204 /*************************************************************************/
6209 template<class OStream>
6210 void print(OStream& os, const pattern& param, int level = 0)
6211 {
6212  if(param.is_group()) {
6213  print(os, param.as_group(), level);
6214  }
6215  else {
6216  os << doc_string(4*level, ' ');
6217  print(os, param.as_param());
6218  }
6219 }
6220 
6221 
6222 /*************************************************************************/
6227 template<class OStream>
6228 void print(OStream& os, const group& g, int level)
6229 {
6230  auto indent = doc_string(4*level, ' ');
6231  os << indent;
6232  if(g.blocking()) os << '!';
6233  if(g.joinable()) os << 'J';
6234  os << (g.exclusive() ? "(|\n" : "(\n");
6235  for(const auto& p : g) {
6236  print(os, p, level+1);
6237  }
6238  os << '\n' << indent << (g.exclusive() ? "|)" : ")");
6239  if(g.repeatable()) os << "...";
6240  os << '\n';
6241 }
6242 
6243 
6244 } // namespace debug
6245 } //namespace clipp
6246 
6247 #endif
6248 
void notify_conflict(arg_index idx) const
executes conflict error actions
Definition: clipp.h:1409
std::vector< arg_string > arg_list
Definition: clipp.h:58
const group & parent() const noexcept
Definition: clipp.h:3772
friend OStream & operator<<(OStream &os, const usage_lines &p)
Definition: clipp.h:5342
subrange first_integer_match(std::basic_string< C, T, A > s, C digitSeparator=C(','))
returns first substring match (pos,len) that represents an integer (with optional digit separators)
Definition: clipp.h:977
static unsigned long int from(const char *s)
Definition: clipp.h:376
bool any_blocking() const
returns true if any child is blocking
Definition: clipp.h:3067
stores strings for man page sections
Definition: clipp.h:5983
group()=default
bool joinable() const noexcept
returns if a command line argument can be matched by a combination of (partial) matches through any n...
Definition: clipp.h:2992
bool exclusive() const noexcept
returns if children are mutually exclusive alternatives
Definition: clipp.h:3023
def Action(kernel, nam, shared=False)
Definition: DDG4.py:181
parameter opt_values(const doc_string &label, Targets &&... tgts)
makes optional, blocking, repeatable value parameter; matches any non-empty string
Definition: clipp.h:2184
const parameter * param() const noexcept
Definition: clipp.h:4249
group & scoped(bool yes)
turns explicit scoping on or off operators , & | and other combinating functions will not merge group...
Definition: clipp.h:3002
man page section
Definition: clipp.h:5990
parameter opt_word(const doc_string &label, Targets &&... tgts)
makes optional, blocking value parameter; matches any string consisting of alphanumeric characters
Definition: clipp.h:2250
friend parameter & with_prefixes_short_long(const arg_string &shortpfx, const arg_string &longpfx, parameter &p)
prepend prefix to each flag
Definition: clipp.h:1992
group::depth_first_traverser dfs_traverser
Definition: clipp.h:3758
constexpr assign_value(T &target, X &&value) noexcept
Definition: clipp.h:470
size_type size() const noexcept
returns number of children
Definition: clipp.h:3222
bool joinable() const noexcept
inside group with joinable flags
Definition: clipp.h:2741
constexpr size_type at() const noexcept
position of the match within the subject string
Definition: clipp.h:100
subrange longest_prefix_match(const std::basic_string< C, T, A > &arg, const InputRange &prefixes)
returns longest prefix range that could be found in 'arg'
Definition: clipp.h:863
const string & repeat_postfix() const noexcept
Definition: clipp.h:5157
const string & group_prefix() const noexcept
Definition: clipp.h:5186
parameter && with_prefix(const arg_string &prefix, parameter &&p)
recursively prepends a prefix to all flags
Definition: clipp.h:3641
group(parameter param, Params... params)
Definition: clipp.h:2955
const string & alternative_flags_prefix() const noexcept
Definition: clipp.h:5176
const match_function & matcher() const noexcept
access custom match operation
Definition: clipp.h:1970
T clamped_on_limits(const V &v)
returns value of v as a T, clamped at T's maximum
Definition: clipp.h:327
bool operator()(const arg_string &arg)
processes one command line argument
Definition: clipp.h:4298
child & operator[](size_type index) noexcept
indexed, nutable access to child
Definition: clipp.h:3197
const arg_string & str() const noexcept
Definition: clipp.h:4056
constexpr auto check_has_size_getter(int) -> decltype(std::declval< T >().size(), std::true_type
size() member type trait
Definition: clipp.h:253
group one_of(Param param, Params... params)
makes a group of alternative parameters or groups
Definition: clipp.h:3413
parameter value(const doc_string &label, Targets &&... tgts)
makes required, blocking, repeatable value parameter; matches any non-empty string
Definition: clipp.h:2094
def Filter(kernel, nam, shared=False)
Definition: DDG4.py:185
const arg_mappings & args() const
returns range of argument -> parameter mappings
Definition: clipp.h:4322
primary namespace
Definition: clipp.h:36
man_page & append_section(string title, string content)
Definition: clipp.h:6018
detail::scoped_dfs_traverser scoped_dfs_traverser
Definition: clipp.h:4180
auto longest_common_prefix(const InputRange &strs) -> typename std::decay< decltype(*begin(strs))>::type
returns longest common prefix of several sequential random access containers
Definition: clipp.h:780
string str() const
Definition: clipp.h:5811
group of parameters and/or other groups; can be configured to act as a group of alternatives (exclusi...
Definition: clipp.h:2447
const string & repeat_prefix() const noexcept
Definition: clipp.h:5156
group operator|(parameter a, parameter b)
makes a group of alternative parameters or groups
Definition: clipp.h:3425
bool alphabetic(const arg_string &s)
predicate that returns true if the argument is a non-empty string that consists only of alphabetic ch...
Definition: clipp.h:1616
const string & alternative_group_separator() const noexcept
Definition: clipp.h:5120
depth_first_traverser & next_sibling()
go to next sibling of current
Definition: clipp.h:2812
missing_events missed() const
returns list of missing events
Definition: clipp.h:4327
std::vector< arg_mapping > arg_mappings
Definition: clipp.h:4260
int arg_index
Definition: clipp.h:53
const child & operator*() const noexcept
Definition: clipp.h:2781
bool is_repeatable(int minlevel=0) const noexcept
repeatable or inside a repeatable group >= minlevel
Definition: clipp.h:2732
iterator end() const noexcept
returns non-mutating iterator to position one past the last argument -> parameter mapping
Definition: clipp.h:4811
integers(char digitSeparator=' ')
Definition: clipp.h:1662
bool empty() const noexcept
returns true, if group has no children, false otherwise
Definition: clipp.h:3219
const group * repeat_group() const noexcept
innermost repeat group
Definition: clipp.h:2754
match_t prefix_match(scoped_dfs_traverser pos, const arg_string &arg, const Predicate &select)
finds the first parameter that matches any (non-empty) prefix of a given string; candidate parameters...
Definition: clipp.h:4107
const string & label_prefix() const noexcept
Definition: clipp.h:5136
bool represents_number(const std::basic_string< C, T, A > &candidate, C digitSeparator=C(','), C decimalPoint=C('.'), C exponential=C('e'))
returns true if candidate string represents a number
Definition: clipp.h:1014
bool empty() const noexcept
Definition: clipp.h:6041
static unsigned long long int from(const char *s)
Definition: clipp.h:384
parser::missing_event missing_event
Definition: clipp.h:4748
bool is_last_in_group() const noexcept
Definition: clipp.h:2706
predicate that returns the first substring match within the input string that rmeepresents a number (...
Definition: clipp.h:1630
subrange operator()(const arg_string &s) const
Definition: clipp.h:1710
const pattern * operator->() const noexcept
Definition: clipp.h:3776
T & operator%(doc_string docstr, token< T > &p)
sets documentation strings on a token
Definition: clipp.h:1502
tri blocking() const noexcept
Definition: clipp.h:5023
const arg_list & flags() const noexcept
access range of flag strings
Definition: clipp.h:1964
bool flags_are_prefix_free() const
returns true, if no flag occurs as true prefix of any other flag (identical flags will be ignored)
Definition: clipp.h:3283
detail::assign_value< T, V > set(T &target, V value)
makes function object with a const char* parameter that assigns a value to a ref-captured object
Definition: clipp.h:1055
arg_index after_index() const noexcept
Definition: clipp.h:4251
decrements using operator –
Definition: clipp.h:537
depth_first_traverser & back_to_parent()
Definition: clipp.h:2855
substring(arg_string str)
Definition: clipp.h:1708
const_iterator cend() const noexcept
returns non-mutating iterator to position one past the last element
Definition: clipp.h:3248
usage_lines & ommit_outermost_group_surrounders(bool yes)
Definition: clipp.h:5333
subrange longest_substring_match(const std::basic_string< C, T, A > &arg, const InputRange &substrings)
returns longest substring range that could be found in 'arg'
Definition: clipp.h:827
Derived & if_blocked(simple_action a)
adds an action that will be called if a parameter was matched, but was unreachable in the current sco...
Definition: clipp.h:1272
mixin that provides basic common settings of parameters and groups
Definition: clipp.h:1434
size_type size() const noexcept
Definition: clipp.h:6039
parameter values(const doc_string &label, Targets &&... tgts)
makes required, blocking, repeatable value parameter; matches any non-empty string
Definition: clipp.h:2124
Derived & if_repeated(simple_action a)
adds an action that will be called if a parameter matches an argument for the 2nd,...
Definition: clipp.h:1232
bool operator()(const parameter &) const noexcept
Definition: clipp.h:4031
int line_spacing() const noexcept
Definition: clipp.h:5221
man_page make_man_page(const group &params, doc_string progname="", const doc_formatting &fmt=doc_formatting{})
generates man sections from command line parameters with sections "synopsis" and "options"
Definition: clipp.h:6086
group & push_front(const parameter &v)
adds child parameter at the beginning
Definition: clipp.h:3146
parameter repeatable(parameter p)
makes a repeatable copy of a parameter
Definition: clipp.h:3592
int level() const noexcept
Definition: clipp.h:2697
bool merge_joinable_with_common_prefix() const noexcept
Definition: clipp.h:5248
doc_formatting & max_flags_per_param_in_doc(int max)
determines maximum number of flags per parameter to be printed in detailed parameter documentation li...
Definition: clipp.h:5201
int max_flags_per_param_in_doc() const noexcept
Definition: clipp.h:5205
const group * join_group() const noexcept
outermost join group
Definition: clipp.h:2763
scoped_dfs_traverser & operator++()
Definition: clipp.h:3805
friend Derived & operator<<(Target &&t, Derived &p)
adds target, see member function 'target'
Definition: clipp.h:1357
int start_column() const noexcept
Definition: clipp.h:5075
const string & optional_prefix() const noexcept
Definition: clipp.h:5146
const pattern * ptr() const noexcept
Definition: clipp.h:3779
doc_formatting & start_column(int col)
determines column where documentation printing starts
Definition: clipp.h:5074
group::depth_first_traverser dfs_traverser
Definition: clipp.h:4179
const string & content() const noexcept
Definition: clipp.h:5999
parameter opt_numbers(const doc_string &label, Targets &&... tgts)
makes optional, blocking, repeatable value parameter; matches any string that represents a number
Definition: clipp.h:2340
const string & alternative_flags_postfix() const noexcept
Definition: clipp.h:5177
static bool from(const char *s)
Definition: clipp.h:344
tri required() const noexcept
Definition: clipp.h:5019
void next_after_match(scoped_dfs_traverser match)
Definition: clipp.h:3845
constexpr auto check_is_input_range(int) -> decltype(begin(std::declval< T >()), end(std::declval< T >()), std::true_type
input range type trait
Definition: clipp.h:225
constexpr auto check_is_callable_without_arg(int) -> decltype(std::declval< Fn >()(), std::integral_constant< bool, std::is_same< Ret, typename std::result_of< Fn()>::type >::value >
Definition: clipp.h:160
constexpr map_arg_to(T &target) noexcept
Definition: clipp.h:588
const_iterator begin() const noexcept
Definition: clipp.h:6045
DFS traverser that keeps track of 'scopes' scope = all parameters that are either bounded by two bloc...
Definition: clipp.h:3756
bool any_bad_repeat() const noexcept
returns if any parameter matched repeatedly although it was not allowed to
Definition: clipp.h:4787
group & merge(group &&g)
adds all children of other group at the end
Definition: clipp.h:3176
constexpr auto check_is_callable(int) -> decltype(std::declval< Fn >()(std::declval< Args >()...), std::integral_constant< bool, std::is_same< Ret, typename std::result_of< Fn(Args...)>::type >::value >
function (class) signature type trait
Definition: clipp.h:149
const context_list & stack() const
Definition: clipp.h:2748
void operator()(const char *s) const
Definition: clipp.h:590
const section & operator[](size_type index) const noexcept
Definition: clipp.h:6034
group in_sequence(Param param, Params... params)
makes a parameter/group sequence by making all input objects blocking
Definition: clipp.h:3484
predicate that returns true if the input string does not start with a given prefix
Definition: clipp.h:1747
Derived & call(arg_action a)
adds an action that has an operator() that is callable with a 'const char*' argument
Definition: clipp.h:1190
parameter integers(const doc_string &label, Targets &&... tgts)
makes required, blocking, repeatable value parameter; matches any string that represents an integer
Definition: clipp.h:2376
group & joinable(group &param)
makes a group of parameters and/or groups where all single char flag params ("-a",...
Definition: clipp.h:3552
bool all_flagless() const
returns if all children are value parameters (recursive)
Definition: clipp.h:3091
const string & param_separator() const noexcept
Definition: clipp.h:5099
parser::arg_mappings arg_mappings
Definition: clipp.h:4747
bool conflict() const noexcept
Definition: clipp.h:4217
bool any_error() const noexcept
returns true if any parsing error / violation of the command line interface definition occured
Definition: clipp.h:4794
bool none(const arg_string &)
predicate that is always false
Definition: clipp.h:1579
void operator()() const
Definition: clipp.h:520
parameter option(String &&flag, Strings &&... flags)
makes optional, non-blocking exact match parameter
Definition: clipp.h:2078
arg_list all_flags() const
returns range of all flags (recursive)
Definition: clipp.h:3274
numbers(char decimalPoint='.', char digitSeparator=' ', char exponentSeparator='e')
Definition: clipp.h:1633
void trim(std::basic_string< C, T, A > &s)
removes leading and trailing whitespace from string
Definition: clipp.h:712
depth_first_traverser & skip_alternatives()
skips all other alternatives in surrounding exclusive groups on next ++ note: renders alternatives un...
Definition: clipp.h:2877
bool has_prefix(const std::basic_string< C, T, A > &subject, const std::basic_string< C, T, A > &prefix)
returns true, if the 'prefix' argument is a prefix of the 'subject' argument
Definition: clipp.h:744
friend OStream & operator<<(OStream &os, const documentation &p)
Definition: clipp.h:5805
const string & title() const noexcept
Definition: clipp.h:5998
const_iterator cbegin() const noexcept
returns non-mutating iterator to position of first element
Definition: clipp.h:3241
string str() const
Definition: clipp.h:5347
constexpr increment(T &target) noexcept
Definition: clipp.h:518
scoped_dfs_traverser & next_sibling()
Definition: clipp.h:3795
detail::increment< T > increment(T &target)
makes function object that increments using operator ++
Definition: clipp.h:1119
parser(const group &root, arg_index offset=0)
initializes parser with a command line interface
Definition: clipp.h:4284
const string & empty_label() const noexcept
Definition: clipp.h:5092
arg_string common_flag_prefix() const
Definition: clipp.h:3784
int indent_size() const noexcept
Definition: clipp.h:5084
bool operator()(const arg_string &s) const
Definition: clipp.h:1752
section(string stitle, string scontent)
Definition: clipp.h:5994
doc_formatting & surround_optional(const string &prefix, const string &postfix)
determnines strings surrounding optional parameters/groups
Definition: clipp.h:5141
const doc_string & label() const
returns parameter label; will be used for documentation, if flags are empty
Definition: clipp.h:1917
void notify_blocked(arg_index idx) const
executes blocked error actions
Definition: clipp.h:1405
arg_mappings::const_iterator iterator
Definition: clipp.h:4750
predicate that returns true if the input string represents an integer (with optional digit separators...
Definition: clipp.h:1659
depth_first_traverser begin_dfs() const noexcept
returns augmented iterator for depth first searches
Definition: clipp.h:3256
void triml(std::basic_string< C, T, A > &s)
removes leading whitespace from string
Definition: clipp.h:693
const parameter * param() const noexcept
Definition: clipp.h:4209
OStream & operator<<(OStream &os, const man_page &man)
generates man page based on command line parameters
Definition: clipp.h:6105
int paragraph_spacing() const noexcept
Definition: clipp.h:5230
depth_first_traverser & next_alternative()
skips to next alternative in innermost group
Definition: clipp.h:2838
subrange match(const arg_string &arg) const
returns either longest matching prefix of 'arg' in any of the flags or the result of the custom match...
Definition: clipp.h:1945
documentation(const group &cli, const doc_formatting &fmt=doc_formatting{}, const param_filter &filter=param_filter{})
Definition: clipp.h:5785
bool all_required() const
returns true, if all children are required to match
Definition: clipp.h:3036
references a non-matched, required parameter
Definition: clipp.h:4242
parameter words(const doc_string &label, Targets &&... tgts)
makes required, blocking, repeatable value parameter; matches any string consisting of alphanumeric c...
Definition: clipp.h:2232
void print(OStream &os, const parsing_result &result)
prints parsing result
Definition: clipp.h:6161
constexpr bool operator!=(tri t, bool b) noexcept
Definition: clipp.h:73
makes a value from a string and assigns it to an object
Definition: clipp.h:585
children_store::size_type size_type
Definition: clipp.h:2654
parameter opt_integer(const doc_string &label, Targets &&... tgts)
makes optional, blocking value parameter; matches any string that represents an integer
Definition: clipp.h:2394
subrange operator()(const arg_string &s) const
Definition: clipp.h:1641
children_store::iterator iterator
Definition: clipp.h:2653
doc_string doc_label(const parameter &p)
prints first flag or value label of a parameter
Definition: clipp.h:6137
doc_formatting & surround_alternative_flags(const string &prefix, const string &postfix)
determnines strings surrounding alternative flags
Definition: clipp.h:5171
const scoped_dfs_traverser & pos() const noexcept
Definition: clipp.h:4057
bool ommit_outermost_group_surrounders() const
Definition: clipp.h:5337
result of a matching operation
Definition: clipp.h:4049
parsing_result parse(arg_list args, const group &cli)
parses vector of arg strings and executes actions
Definition: clipp.h:4941
recursively iterates over all nodes
Definition: clipp.h:2662
predicate that returns true if the length of the input string is wihtin a given interval
Definition: clipp.h:1772
generates usage lines
Definition: clipp.h:5316
const group * root() const noexcept
Definition: clipp.h:2769
length(std::size_t exact)
Definition: clipp.h:1775
bool any(const arg_string &)
predicate that is always true
Definition: clipp.h:1571
group operator&(parameter a, parameter b)
makes a parameter/group sequence by making all input objects blocking
Definition: clipp.h:3497
bool any_required() const
returns true, if any child is required to match
Definition: clipp.h:3030
(start,size) index range
Definition: clipp.h:83
group & exclusive(bool yes)
determines if children are mutually exclusive alternatives
Definition: clipp.h:3018
const group * join_group() const noexcept
Definition: clipp.h:3774
bool is_alternative(int minlevel=0) const noexcept
inside a group of alternatives >= minlevel
Definition: clipp.h:2723
Derived & if_missing(simple_action a)
adds an action that will be called if a required parameter is missing
Definition: clipp.h:1252
arg_mappings::size_type unmapped_args_count() const noexcept
returns number of arguments that could not be mapped to a parameter
Definition: clipp.h:4765
const string & alternatives_postfix() const noexcept
Definition: clipp.h:5167
std::string doc_string
Definition: clipp.h:56
bool start_of_repeat_group() const noexcept
Definition: clipp.h:3791
const string & program_name() const noexcept
Definition: clipp.h:6058
const arg_string & arg() const noexcept
Definition: clipp.h:4207
parsing_result()=default
default: empty redult
const string & optional_postfix() const noexcept
Definition: clipp.h:5147
const group & parent() const noexcept
Definition: clipp.h:2791
group & push_back(const parameter &v)
adds child parameter at the end
Definition: clipp.h:3103
bool fwd_to_unsigned_int(const char *&s)
forwards string to first non-whitespace char; std string -> unsigned conv yields max value,...
Definition: clipp.h:287
const missing_events & missing() const noexcept
access to range of missing parameter match events
Definition: clipp.h:4804
iterator end() noexcept
returns mutating iterator to position one past the last element
Definition: clipp.h:3244
constexpr decrement(T &target) noexcept
Definition: clipp.h:540
arg_mapping(arg_index idx, arg_string s, const dfs_traverser &match)
Definition: clipp.h:4191
size_type depth() const
returns number of nested levels; 1 for a flat group
Definition: clipp.h:3225
void operator()() const
Definition: clipp.h:498
parser::arg_mapping arg_mapping
Definition: clipp.h:4746
bool blocked() const noexcept
Definition: clipp.h:4216
parser::missing_events missing_events
Definition: clipp.h:4749
doc_string string
Definition: clipp.h:5318
length min_length(std::size_t min)
makes function object that returns true if the input string has a given minimum length
Definition: clipp.h:1800
const arg_string & prefix() const noexcept
Definition: clipp.h:5015
section_store::const_iterator const_iterator
Definition: clipp.h:6012
prefix_not(arg_string p)
Definition: clipp.h:1750
iterator begin() const noexcept
returns non-mutating iterator to position of first argument -> parameter mapping
Definition: clipp.h:4808
predicate that returns true if the input string starts with a given prefix
Definition: clipp.h:1726
subrange operator()(const arg_string &s) const
Definition: clipp.h:1664
iterator begin() noexcept
returns mutating iterator to position of first element
Definition: clipp.h:3237
parameter integer(const doc_string &label, Targets &&... tgts)
makes required, blocking value parameter; matches any string that represents an integer
Definition: clipp.h:2358
constexpr flip_bool(bool &target) noexcept
Definition: clipp.h:494
value limits clamping
Definition: clipp.h:303
void operator()() const
Definition: clipp.h:566
bool bad_repeat() const noexcept
Definition: clipp.h:4219
group & joinable(bool yes)
determines if a command line argument can be matched by a combination of (partial) matches through an...
Definition: clipp.h:2984
static unsigned char from(const char *s)
Definition: clipp.h:352
constexpr size_type length() const noexcept
length of the matching subsequence
Definition: clipp.h:102
const dfs_traverser & last_match() const noexcept
Definition: clipp.h:3770
bool all_optional() const
returns true if all children are optional (=non-required)
Definition: clipp.h:3049
constexpr auto check_is_void_callable(int) -> decltype(std::declval< Fn >()(std::declval< Args >()...), std::true_type
Definition: clipp.h:173
bool operator()(const parameter &p) const noexcept
Definition: clipp.h:4031
bool alphanumeric(const arg_string &s)
predicate that returns true if the argument is a non-empty string that consists only of alphanumeric ...
Definition: clipp.h:1602
bool any_conflict() const noexcept
returns if any argument matched more than one parameter that were mutually exclusive
Definition: clipp.h:4780
contains argument -> parameter mappings and missing parameters
Definition: clipp.h:4744
mixin that provides action definition and execution
Definition: clipp.h:1165
depth_first_traverser & skip_siblings()
don't visit next siblings, go back to parent on next ++ note: renders siblings unreachable for *this
Definition: clipp.h:2865
size_type param_count() const
returns recursive parameter count
Definition: clipp.h:3263
man_page & program_name(const string &n)
Definition: clipp.h:6050
increments using operator ++
Definition: clipp.h:515
bool repeatable() const noexcept
returns if a group/parameter is repeatable
Definition: clipp.h:1461
static T from(const V &v)
Definition: clipp.h:304
tri repeatable() const noexcept
Definition: clipp.h:5027
void remove_ws(std::basic_string< C, T, A > &s)
removes all whitespaces from string
Definition: clipp.h:726
child & front() noexcept
mutable access to first child
Definition: clipp.h:3207
arg_string::size_type size_type
Definition: clipp.h:85
std::size_t repeat() const noexcept
Definition: clipp.h:4214
parameter command(String &&flag, Strings &&... flags)
makes required non-blocking exact match parameter
Definition: clipp.h:2048
int alternatives_min_split_size() const noexcept
Definition: clipp.h:5269
group(group p1, P2 p2, Ps... ps)
Definition: clipp.h:2963
length max_length(std::size_t max)
makes function object that returns true if the input string is not longer than a given maximum length
Definition: clipp.h:1811
T & doc(doc_string docstr, token< T > &p)
sets documentation strings on a token
Definition: clipp.h:1539
int doc_column() const noexcept
Definition: clipp.h:5079
friend Derived & operator>>(Derived &p, Target &&t)
adds target, see member function 'target'
Definition: clipp.h:1373
parameter opt_value(const doc_string &label, Targets &&... tgts)
makes optional, blocking value parameter; matches any non-empty string
Definition: clipp.h:2154
bool scoped() const noexcept
returns true if operators , & | and other combinating functions will merge groups and false otherwise
Definition: clipp.h:3010
static unsigned short int from(const char *s)
Definition: clipp.h:360
constexpr bool operator==(tri t, bool b) noexcept
Definition: clipp.h:69
doc_formatting & surround_labels(const string &prefix, const string &postfix)
determnines strings surrounding parameter labels
Definition: clipp.h:5131
doc_formatting & surround_group(const string &prefix, const string &postfix)
determnines strings surrounding non-exclusive groups
Definition: clipp.h:5181
detail::flip_bool flip(bool &b)
makes function object that flips the value of a ref-captured bool
Definition: clipp.h:1104
static unsigned int from(const char *s)
Definition: clipp.h:368
child value_type
Definition: clipp.h:2646
const child * param() const noexcept
Definition: clipp.h:2683
std::vector< missing_event > missing_events
Definition: clipp.h:4259
constexpr subrange() noexcept
default: no match
Definition: clipp.h:89
parameter required(String &&flag, Strings &&... flags)
makes required non-blocking exact match parameter
Definition: clipp.h:2063
int section_row_spacing() const noexcept
Definition: clipp.h:6068
std::vector< context > context_list
Definition: clipp.h:2674
bool required() const noexcept
returns if a parameter is required
Definition: clipp.h:1900
tri has_doc() const noexcept
Definition: clipp.h:5031
doc_formatting & max_flags_per_param_in_usage(int max)
determines maximum number of flags per parameter to be printed in usage lines
Definition: clipp.h:5209
const string & group_postfix() const noexcept
Definition: clipp.h:5187
friend bool operator!=(const depth_first_traverser &a, const depth_first_traverser &b)
Definition: clipp.h:2911
parameter opt_integers(const doc_string &label, Targets &&... tgts)
makes optional, blocking, repeatable value parameter; matches any string that represents an integer
Definition: clipp.h:2412
bool matched() const noexcept
Definition: clipp.h:3789
subrange first_number_match(std::basic_string< C, T, A > s, C digitSeparator=C(','), C decimalPoint=C('.'), C exponential=C('e'))
returns first substring match (pos,len) within the input string that represents a number (with at max...
Definition: clipp.h:917
arg_index index() const noexcept
Definition: clipp.h:4206
const_iterator end() const noexcept
Definition: clipp.h:6046
bool split_alternatives() const noexcept
Definition: clipp.h:5259
group operator,(parameter a, parameter b)
makes a group of parameters and/or groups
Definition: clipp.h:3369
void set_blocking(bool)
Definition: clipp.h:3466
man_page & prepend_section(string title, string content)
Definition: clipp.h:6025
void notify_repeated(arg_index idx) const
executes repeat actions
Definition: clipp.h:1397
bool blocking() const noexcept
returns if a group/parameter is blocking/positional
Definition: clipp.h:1474
bool any_optional() const
returns true if any child is optional (=non-required)
Definition: clipp.h:3045
Derived & set(Target &t)
adds an action that will set the value of 't' from a 'const char*' arg
Definition: clipp.h:1215
friend parameter & with_prefix(const arg_string &prefix, parameter &p)
prepend prefix to each flag
Definition: clipp.h:1978
std::function< bool(const arg_string &)> match_predicate
match predicates
Definition: clipp.h:126
bool operator()(const parameter &p) const noexcept
returns true, if parameter satisfies all filters
Definition: clipp.h:5035
bool joinable() const noexcept
Definition: clipp.h:3783
detail::decrement< T > decrement(T &target)
makes function object that increments by a fixed amount using operator +=
Definition: clipp.h:1141
clipp::doc_string doc_string
Definition: clipp.h:1437
bool represents_integer(const std::basic_string< C, T, A > &candidate, C digitSeparator=C(','))
returns true if candidate string represents an integer
Definition: clipp.h:1033
depth_first_traverser & next_after_siblings()
go to next position after siblings of current
Definition: clipp.h:2828
const child * operator->() const noexcept
Definition: clipp.h:2786
Derived & operator()(arg_action a)
adds an action that has an operator() that is callable with a 'const char*' argument
Definition: clipp.h:1204
usage_lines(const group &params, string prefix="", const doc_formatting &fmt=doc_formatting{})
Definition: clipp.h:5320
bool operator()(const arg_string &s) const
Definition: clipp.h:1784
documentation formatting options
Definition: clipp.h:5069
predicate that returns true if the input string contains a given substring
Definition: clipp.h:1705
const pattern & operator*() const noexcept
Definition: clipp.h:3777
constexpr increment_by(T &target, T by) noexcept
Definition: clipp.h:562
doc_string string
Definition: clipp.h:5071
assigns boolean constant to one or multiple target objects
Definition: clipp.h:466
Derived & if_conflicted(simple_action a)
adds an action that will be called if a parameter match was in conflict with a different alternative ...
Definition: clipp.h:1293
void undo(const memento &m)
Definition: clipp.h:2925
scoped_dfs_traverser & next_after_siblings()
Definition: clipp.h:3801
const dfs_traverser & base() const noexcept
Definition: clipp.h:3769
bool all_blocking() const
returns true if all children is blocking
Definition: clipp.h:3074
bool valid() const noexcept
returns false if previously processed command line arguments lead to an invalid / inconsistent parsin...
Definition: clipp.h:4342
bool has_postfix(const std::basic_string< C, T, A > &subject, const std::basic_string< C, T, A > &postfix)
returns true, if the 'postfix' argument is a postfix of the 'subject' argument
Definition: clipp.h:760
void notify_missing(arg_index idx) const
executes missing error actions
Definition: clipp.h:1401
bool any_flagless() const
returns if any child is a value parameter (recursive)
Definition: clipp.h:3083
const string & label_postfix() const noexcept
Definition: clipp.h:5137
doc_formatting & surround_repeat(const string &prefix, const string &postfix)
determnines strings surrounding repeatable parameters/groups
Definition: clipp.h:5151
match_t full_match(scoped_dfs_traverser pos, const arg_string &arg, const Predicate &select)
finds the first parameter that matches a given string candidate parameters are traversed using a scop...
Definition: clipp.h:4076
const string & flag_separator() const noexcept
Definition: clipp.h:5127
const string & group_separator() const noexcept
Definition: clipp.h:5106
const string & joinable_prefix() const noexcept
Definition: clipp.h:5196
const group * repeat_group() const noexcept
Definition: clipp.h:3773
command line parameter that can match one or many arguments.
Definition: clipp.h:1831
detail::assign_value< bool > unset(bool &target)
makes function object that sets a bool to false
Definition: clipp.h:1094
parameter number(const doc_string &label, Targets &&... tgts)
makes required, blocking value parameter; matches any string that represents a number
Definition: clipp.h:2286
type conversion helpers
Definition: clipp.h:340
bool merge_alternative_flags_with_common_prefix() const noexcept
Definition: clipp.h:5238
generates parameter and group documentation from docstrings
Definition: clipp.h:5781
constexpr bool prefix() const noexcept
returns true, if query string is a prefix of the subject string
Definition: clipp.h:105
arg_string common_flag_prefix() const
returns longest common prefix of all flags
Definition: clipp.h:3305
constexpr auto check_is_void_callable_without_arg(int) -> decltype(std::declval< Fn >()(), std::true_type
Definition: clipp.h:182
int max_flags_per_param_in_usage() const noexcept
Definition: clipp.h:5213
std::string arg_string
Definition: clipp.h:55
bool any_blocked() const noexcept
returns if any argument could only be matched by an unreachable parameter
Definition: clipp.h:4773
prefix(arg_string p)
Definition: clipp.h:1729
bool blocking() const noexcept
returns if the entire group is blocking / positional
Definition: clipp.h:3056
parameter opt_number(const doc_string &label, Targets &&... tgts)
makes optional, blocking value parameter; matches any string that represents a number
Definition: clipp.h:2322
scoped_dfs_traverser & next_alternative()
Definition: clipp.h:3798
positive_integers(char digitSeparator=' ')
Definition: clipp.h:1683
parameter opt_words(const doc_string &label, Targets &&... tgts)
makes optional, blocking, repeatable value parameter; matches any string consisting of alphanumeric c...
Definition: clipp.h:2268
filter predicate for parameters and groups; Can be used to limit documentation generation to paramete...
Definition: clipp.h:5005
section_store::size_type size_type
Definition: clipp.h:6013
arg_string common_flag_prefix() const noexcept
common flag prefix of all flags in current group
Definition: clipp.h:2774
const doc_string & doc() const noexcept
returns documentation string
Definition: clipp.h:1442
bool operator()(const parameter &p) const noexcept
Definition: clipp.h:4037
parameter()
makes default parameter, that will match nothing
Definition: clipp.h:1848
child_t< parameter, group > child
Definition: clipp.h:2645
parameter any_other(Targets &&... tgts)
makes catch-all value parameter
Definition: clipp.h:2429
T make(const arg_string &s)
converts string to value of target type 'T'
Definition: clipp.h:661
subrange substring_match(const std::basic_string< C, T, A > &subject, const std::basic_string< C, T, A > &query)
returns the first occurrence of 'query' within 'subject'
Definition: clipp.h:897
parameter numbers(const doc_string &label, Targets &&... tgts)
makes required, blocking, repeatable value parameter; matches any string that represents a number
Definition: clipp.h:2304
children_store::const_iterator const_iterator
Definition: clipp.h:2652
tri
tristate
Definition: clipp.h:67
child & back() noexcept
mutable access to last child
Definition: clipp.h:3212
const string & alternative_param_separator() const noexcept
Definition: clipp.h:5113
default command line arguments parser
Definition: clipp.h:4177
arg_index parse_count() const noexcept
returns number of processed command line arguments
Definition: clipp.h:4337
void operator()() const
Definition: clipp.h:542
const string & alternatives_prefix() const noexcept
Definition: clipp.h:5166
match_t partial_match(scoped_dfs_traverser pos, const arg_string &arg, const Predicate &select)
finds the first parameter that partially matches a given string; candidate parameters are traversed u...
Definition: clipp.h:4143
const string & joinable_postfix() const noexcept
Definition: clipp.h:5197
bool operator()(const arg_string &s) const
Definition: clipp.h:1731
bool any_error() const noexcept
Definition: clipp.h:4225
void trimr(std::basic_string< C, T, A > &s)
removes trailing whitespace from string
Definition: clipp.h:675
group::child pattern
group or parameter
Definition: clipp.h:3359
void execute_actions(const arg_string &arg) const
executes all argument actions
Definition: clipp.h:1388
bool is_first_in_group() const noexcept
Definition: clipp.h:2701
increments by a fixed amount using operator +=
Definition: clipp.h:559
std::function< subrange(const arg_string &)> match_function
Definition: clipp.h:127
parameter word(const doc_string &label, Targets &&... tgts)
makes required, blocking value parameter; matches any string consisting of alphanumeric characters
Definition: clipp.h:2214
void operator()() const
Definition: clipp.h:474
depth_first_traverser & operator++()
go to next element of depth first search
Definition: clipp.h:2798
doc_formatting & surround_alternatives(const string &prefix, const string &postfix)
determnines strings surrounding exclusive groups
Definition: clipp.h:5161
bool is_last_in_path() const noexcept
Definition: clipp.h:2711
doc_string string
Definition: clipp.h:5783
doc_formatting & surround_joinable(const string &prefix, const string &postfix)
determnines strings surrounding joinable groups
Definition: clipp.h:5191
missing_event(const parameter *p, arg_index after)
Definition: clipp.h:4245
bool nonempty(const arg_string &s)
predicate that returns true if the argument string is non-empty string
Definition: clipp.h:1589
subrange operator()(const arg_string &s) const
Definition: clipp.h:1685
predicate that returns true if the input string represents a non-negative integer (with optional digi...
Definition: clipp.h:1680
group & operator=(const group &)=default
friend bool operator==(const depth_first_traverser &a, const depth_first_traverser &b)
Definition: clipp.h:2894
arg -> parameter mapping
Definition: clipp.h:4186
parameter && with_prefixes_short_long(const arg_string &shortpfx, const arg_string &longpfx, parameter &&p)
recursively prepends a prefix to all flags
Definition: clipp.h:3687