Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 10:18:18

0001 /*****************************************************************************
0002  *  ___  _    _   ___ ___
0003  * |  _|| |  | | | _ \ _ \   CLIPP - command line interfaces for modern C++
0004  * | |_ | |_ | | |  _/  _/   version 1.2.3
0005  * |___||___||_| |_| |_|     https://github.com/muellan/clipp
0006  *
0007  * Licensed under the MIT License <http://opensource.org/licenses/MIT>.
0008  * Copyright (c) 2017-2018 André Müller <foss@andremueller-online.de>
0009  *
0010  * ---------------------------------------------------------------------------
0011  * Permission is hereby granted, free of charge, to any person obtaining a
0012  * copy of this software and associated documentation files (the "Software"),
0013  * to deal in the Software without restriction, including without limitation
0014  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
0015  * and/or sell copies of the Software, and to permit persons to whom the
0016  * Software is furnished to do so, subject to the following conditions:
0017  *
0018  * The above copyright notice and this permission notice shall be included in
0019  * all copies or substantial portions of the Software.
0020  *
0021  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
0022  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0023  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
0024  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
0025  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
0026  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
0027  * OTHER DEALINGS IN THE SOFTWARE.
0028  *
0029  *****************************************************************************/
0030 
0031 #ifndef AM_CLIPP_H__
0032 #define AM_CLIPP_H__
0033 
0034 #include <cstring>
0035 #include <string>
0036 #include <cstdlib>
0037 #include <cstring>
0038 #include <cctype>
0039 #include <memory>
0040 #include <vector>
0041 #include <limits>
0042 #include <stack>
0043 #include <algorithm>
0044 #include <sstream>
0045 #include <utility>
0046 #include <iterator>
0047 #include <functional>
0048 
0049 
0050 /*************************************************************************//**
0051  *
0052  * @brief primary namespace
0053  *
0054  *****************************************************************************/
0055 namespace clipp {
0056 
0057 
0058 
0059 /*****************************************************************************
0060  *
0061  * basic constants and datatype definitions
0062  *
0063  *****************************************************************************/
0064 using arg_index = int;
0065 
0066 using arg_string = std::string;
0067 using doc_string = std::string;
0068 
0069 using arg_list  = std::vector<arg_string>;
0070 
0071 
0072 
0073 /*************************************************************************//**
0074  *
0075  * @brief tristate
0076  *
0077  *****************************************************************************/
0078 enum class tri : char { no, yes, either };
0079 
0080 inline constexpr bool operator == (tri t, bool b) noexcept {
0081     return b ? t != tri::no : t != tri::yes;
0082 }
0083 inline constexpr bool operator == (bool b, tri t) noexcept { return  (t == b); }
0084 inline constexpr bool operator != (tri t, bool b) noexcept { return !(t == b); }
0085 inline constexpr bool operator != (bool b, tri t) noexcept { return !(t == b); }
0086 
0087 
0088 
0089 /*************************************************************************//**
0090  *
0091  * @brief (start,size) index range
0092  *
0093  *****************************************************************************/
0094 class subrange {
0095 public:
0096     using size_type = arg_string::size_type;
0097 
0098     /** @brief default: no match */
0099     explicit constexpr
0100     subrange() noexcept :
0101         at_{arg_string::npos}, length_{0}
0102     {}
0103 
0104     /** @brief match length & position within subject string */
0105     explicit constexpr
0106     subrange(size_type pos, size_type len) noexcept :
0107         at_{pos}, length_{len}
0108     {}
0109 
0110     /** @brief position of the match within the subject string */
0111     constexpr size_type at()     const noexcept { return at_; }
0112     /** @brief length of the matching subsequence */
0113     constexpr size_type length() const noexcept { return length_; }
0114 
0115     /** @brief returns true, if query string is a prefix of the subject string */
0116     constexpr bool prefix() const noexcept {
0117         return at_ == 0;
0118     }
0119 
0120     /** @brief returns true, if query is a substring of the query string */
0121     constexpr explicit operator bool () const noexcept {
0122         return at_ != arg_string::npos;
0123     }
0124 
0125 private:
0126     size_type at_;
0127     size_type length_;
0128 };
0129 
0130 
0131 
0132 /*************************************************************************//**
0133  *
0134  * @brief match predicates
0135  *
0136  *****************************************************************************/
0137 using match_predicate = std::function<bool(const arg_string&)>;
0138 using match_function  = std::function<subrange(const arg_string&)>;
0139 
0140 
0141 
0142 
0143 
0144 
0145 /*************************************************************************//**
0146  *
0147  * @brief type traits (NOT FOR DIRECT USE IN CLIENT CODE!)
0148  *        no interface guarantees; might be changed or removed in the future
0149  *
0150  *****************************************************************************/
0151 namespace traits {
0152 
0153 /*************************************************************************//**
0154  *
0155  * @brief function (class) signature type trait
0156  *
0157  *****************************************************************************/
0158 #if defined(__cpp_lib_is_invocable)
0159 template<class Fn, class Ret, class... Args>
0160 constexpr auto
0161 check_is_callable(int) -> decltype(
0162     std::declval<Fn>()(std::declval<Args>()...),
0163     std::integral_constant<bool,
0164         std::is_same<Ret,typename std::invoke_result<Fn,Args...>::type>::value>{} );
0165 
0166 template<class Fn, class Ret>
0167 constexpr auto
0168 check_is_callable_without_arg(int) -> decltype(
0169     std::declval<Fn>()(),
0170     std::integral_constant<bool,
0171         std::is_same<Ret,typename std::invoke_result<Fn>::type>::value>{} );
0172 #else
0173 template<class Fn, class Ret, class... Args>
0174 constexpr auto
0175 check_is_callable(int) -> decltype(
0176     std::declval<Fn>()(std::declval<Args>()...),
0177     std::integral_constant<bool,
0178         std::is_same<Ret,typename std::result_of<Fn(Args...)>::type>::value>{} );
0179 
0180 template<class Fn, class Ret>
0181 constexpr auto
0182 check_is_callable_without_arg(int) -> decltype(
0183     std::declval<Fn>()(),
0184     std::integral_constant<bool,
0185         std::is_same<Ret,typename std::result_of<Fn()>::type>::value>{} );
0186 #endif
0187 
0188 template<class,class,class...>
0189 constexpr auto
0190 check_is_callable(long) -> std::false_type;
0191 
0192 template<class,class>
0193 constexpr auto
0194 check_is_callable_without_arg(long) -> std::false_type;
0195 
0196 
0197 
0198 template<class Fn, class... Args>
0199 constexpr auto
0200 check_is_void_callable(int) -> decltype(
0201     std::declval<Fn>()(std::declval<Args>()...), std::true_type{});
0202 
0203 template<class,class,class...>
0204 constexpr auto
0205 check_is_void_callable(long) -> std::false_type;
0206 
0207 template<class Fn>
0208 constexpr auto
0209 check_is_void_callable_without_arg(int) -> decltype(
0210     std::declval<Fn>()(), std::true_type{});
0211 
0212 template<class>
0213 constexpr auto
0214 check_is_void_callable_without_arg(long) -> std::false_type;
0215 
0216 
0217 
0218 template<class Fn, class Ret>
0219 struct is_callable;
0220 
0221 
0222 template<class Fn, class Ret, class... Args>
0223 struct is_callable<Fn, Ret(Args...)> :
0224     decltype(check_is_callable<Fn,Ret,Args...>(0))
0225 {};
0226 
0227 template<class Fn, class Ret>
0228 struct is_callable<Fn,Ret()> :
0229     decltype(check_is_callable_without_arg<Fn,Ret>(0))
0230 {};
0231 
0232 
0233 template<class Fn, class... Args>
0234 struct is_callable<Fn, void(Args...)> :
0235     decltype(check_is_void_callable<Fn,Args...>(0))
0236 {};
0237 
0238 template<class Fn>
0239 struct is_callable<Fn,void()> :
0240     decltype(check_is_void_callable_without_arg<Fn>(0))
0241 {};
0242 
0243 
0244 
0245 /*************************************************************************//**
0246  *
0247  * @brief input range type trait
0248  *
0249  *****************************************************************************/
0250 template<class T>
0251 constexpr auto
0252 check_is_input_range(int) -> decltype(
0253     begin(std::declval<T>()), end(std::declval<T>()),
0254     std::true_type{});
0255 
0256 template<class T>
0257 constexpr auto
0258 check_is_input_range(char) -> decltype(
0259     std::begin(std::declval<T>()), std::end(std::declval<T>()),
0260     std::true_type{});
0261 
0262 template<class>
0263 constexpr auto
0264 check_is_input_range(long) -> std::false_type;
0265 
0266 template<class T>
0267 struct is_input_range :
0268     decltype(check_is_input_range<T>(0))
0269 {};
0270 
0271 
0272 
0273 /*************************************************************************//**
0274  *
0275  * @brief size() member type trait
0276  *
0277  *****************************************************************************/
0278 template<class T>
0279 constexpr auto
0280 check_has_size_getter(int) ->
0281     decltype(std::declval<T>().size(), std::true_type{});
0282 
0283 template<class>
0284 constexpr auto
0285 check_has_size_getter(long) -> std::false_type;
0286 
0287 template<class T>
0288 struct has_size_getter :
0289     decltype(check_has_size_getter<T>(0))
0290 {};
0291 
0292 } // namespace traits
0293 
0294 
0295 
0296 
0297 
0298 
0299 /*************************************************************************//**
0300  *
0301  * @brief helpers (NOT FOR DIRECT USE IN CLIENT CODE!)
0302  *        no interface guarantees; might be changed or removed in the future
0303  *
0304  *****************************************************************************/
0305 namespace detail {
0306 
0307 
0308 /*************************************************************************//**
0309  * @brief forwards string to first non-whitespace char;
0310  *        std string -> unsigned conv yields max value, but we want 0;
0311  *        also checks for nullptr
0312  *****************************************************************************/
0313 inline bool
0314 fwd_to_unsigned_int(const char*& s)
0315 {
0316     if(!s) return false;
0317     for(; std::isspace(*s); ++s);
0318     if(!s[0] || s[0] == '-') return false;
0319     if(s[0] == '-') return false;
0320     return true;
0321 }
0322 
0323 
0324 /*************************************************************************//**
0325  *
0326  * @brief value limits clamping
0327  *
0328  *****************************************************************************/
0329 template<class T, class V, bool = (sizeof(V) > sizeof(T))>
0330 struct limits_clamped {
0331     static T from(const V& v) {
0332         if(v >= V(std::numeric_limits<T>::max())) {
0333             return std::numeric_limits<T>::max();
0334         }
0335         if(v <= V(std::numeric_limits<T>::lowest())) {
0336             return std::numeric_limits<T>::lowest();
0337         }
0338         return T(v);
0339     }
0340 };
0341 
0342 template<class T, class V>
0343 struct limits_clamped<T,V,false> {
0344     static T from(const V& v) { return T(v); }
0345 };
0346 
0347 
0348 /*************************************************************************//**
0349  *
0350  * @brief returns value of v as a T, clamped at T's maximum
0351  *
0352  *****************************************************************************/
0353 template<class T, class V>
0354 inline T clamped_on_limits(const V& v) {
0355     return limits_clamped<T,V>::from(v);
0356 }
0357 
0358 
0359 
0360 
0361 /*************************************************************************//**
0362  *
0363  * @brief type conversion helpers
0364  *
0365  *****************************************************************************/
0366 template<class T>
0367 struct make {
0368     static inline T from(const char* s) {
0369         if(!s) return false;
0370         //a conversion from const char* to / must exist
0371         return static_cast<T>(s);
0372     }
0373 };
0374 
0375 template<>
0376 struct make<bool> {
0377     static inline bool from(const char* s) {
0378         if(!s) return false;
0379         return static_cast<bool>(s);
0380     }
0381 };
0382 
0383 template<>
0384 struct make<unsigned char> {
0385     static inline unsigned char from(const char* s) {
0386         if(!fwd_to_unsigned_int(s)) return (0);
0387         return clamped_on_limits<unsigned char>(std::strtoull(s,nullptr,10));
0388     }
0389 };
0390 
0391 template<>
0392 struct make<unsigned short int> {
0393     static inline unsigned short int from(const char* s) {
0394         if(!fwd_to_unsigned_int(s)) return (0);
0395         return clamped_on_limits<unsigned short int>(std::strtoull(s,nullptr,10));
0396     }
0397 };
0398 
0399 template<>
0400 struct make<unsigned int> {
0401     static inline unsigned int from(const char* s) {
0402         if(!fwd_to_unsigned_int(s)) return (0);
0403         return clamped_on_limits<unsigned int>(std::strtoull(s,nullptr,10));
0404     }
0405 };
0406 
0407 template<>
0408 struct make<unsigned long int> {
0409     static inline unsigned long int from(const char* s) {
0410         if(!fwd_to_unsigned_int(s)) return (0);
0411         return clamped_on_limits<unsigned long int>(std::strtoull(s,nullptr,10));
0412     }
0413 };
0414 
0415 template<>
0416 struct make<unsigned long long int> {
0417     static inline unsigned long long int from(const char* s) {
0418         if(!fwd_to_unsigned_int(s)) return (0);
0419         return clamped_on_limits<unsigned long long int>(std::strtoull(s,nullptr,10));
0420     }
0421 };
0422 
0423 template<>
0424 struct make<char> {
0425     static inline char from(const char* s) {
0426         //parse as single character?
0427         const auto n = std::strlen(s);
0428         if(n == 1) return s[0];
0429         //parse as integer
0430         return clamped_on_limits<char>(std::strtoll(s,nullptr,10));
0431     }
0432 };
0433 
0434 template<>
0435 struct make<short int> {
0436     static inline short int from(const char* s) {
0437         return clamped_on_limits<short int>(std::strtoll(s,nullptr,10));
0438     }
0439 };
0440 
0441 template<>
0442 struct make<int> {
0443     static inline int from(const char* s) {
0444         return clamped_on_limits<int>(std::strtoll(s,nullptr,10));
0445     }
0446 };
0447 
0448 template<>
0449 struct make<long int> {
0450     static inline long int from(const char* s) {
0451         return clamped_on_limits<long int>(std::strtoll(s,nullptr,10));
0452     }
0453 };
0454 
0455 template<>
0456 struct make<long long int> {
0457     static inline long long int from(const char* s) {
0458         return (std::strtoll(s,nullptr,10));
0459     }
0460 };
0461 
0462 template<>
0463 struct make<float> {
0464     static inline float from(const char* s) {
0465         return (std::strtof(s,nullptr));
0466     }
0467 };
0468 
0469 template<>
0470 struct make<double> {
0471     static inline double from(const char* s) {
0472         return (std::strtod(s,nullptr));
0473     }
0474 };
0475 
0476 template<>
0477 struct make<long double> {
0478     static inline long double from(const char* s) {
0479         return (std::strtold(s,nullptr));
0480     }
0481 };
0482 
0483 template<>
0484 struct make<std::string> {
0485     static inline std::string from(const char* s) {
0486         return std::string(s);
0487     }
0488 };
0489 
0490 
0491 
0492 /*************************************************************************//**
0493  *
0494  * @brief assigns boolean constant to one or multiple target objects
0495  *
0496  *****************************************************************************/
0497 template<class T, class V = T>
0498 class assign_value
0499 {
0500 public:
0501     template<class X>
0502     explicit constexpr
0503     assign_value(T& target, X&& value) noexcept :
0504         t_{std::addressof(target)}, v_{std::forward<X>(value)}
0505     {}
0506 
0507     void operator () () const {
0508         if(t_) *t_ = v_;
0509     }
0510 
0511 private:
0512     T* t_;
0513     V v_;
0514 };
0515 
0516 
0517 
0518 /*************************************************************************//**
0519  *
0520  * @brief flips bools
0521  *
0522  *****************************************************************************/
0523 class flip_bool
0524 {
0525 public:
0526     explicit constexpr
0527     flip_bool(bool& target) noexcept :
0528         b_{&target}
0529     {}
0530 
0531     void operator () () const {
0532         if(b_) *b_ = !*b_;
0533     }
0534 
0535 private:
0536     bool* b_;
0537 };
0538 
0539 
0540 
0541 /*************************************************************************//**
0542  *
0543  * @brief increments using operator ++
0544  *
0545  *****************************************************************************/
0546 template<class T>
0547 class increment
0548 {
0549 public:
0550     explicit constexpr
0551     increment(T& target) noexcept : t_{std::addressof(target)} {}
0552 
0553     void operator () () const {
0554         if(t_) ++(*t_);
0555     }
0556 
0557 private:
0558     T* t_;
0559 };
0560 
0561 
0562 
0563 /*************************************************************************//**
0564  *
0565  * @brief decrements using operator --
0566  *
0567  *****************************************************************************/
0568 template<class T>
0569 class decrement
0570 {
0571 public:
0572     explicit constexpr
0573     decrement(T& target) noexcept : t_{std::addressof(target)} {}
0574 
0575     void operator () () const {
0576         if(t_) --(*t_);
0577     }
0578 
0579 private:
0580     T* t_;
0581 };
0582 
0583 
0584 
0585 /*************************************************************************//**
0586  *
0587  * @brief increments by a fixed amount using operator +=
0588  *
0589  *****************************************************************************/
0590 template<class T>
0591 class increment_by
0592 {
0593 public:
0594     explicit constexpr
0595     increment_by(T& target, T by) noexcept :
0596         t_{std::addressof(target)}, by_{std::move(by)}
0597     {}
0598 
0599     void operator () () const {
0600         if(t_) (*t_) += by_;
0601     }
0602 
0603 private:
0604     T* t_;
0605     T by_;
0606 };
0607 
0608 
0609 
0610 
0611 /*************************************************************************//**
0612  *
0613  * @brief makes a value from a string and assigns it to an object
0614  *
0615  *****************************************************************************/
0616 template<class T>
0617 class map_arg_to
0618 {
0619 public:
0620     explicit constexpr
0621     map_arg_to(T& target) noexcept : t_{std::addressof(target)} {}
0622 
0623     void operator () (const char* s) const {
0624         if(t_ && s) *t_ = detail::make<T>::from(s);
0625     }
0626 
0627 private:
0628     T* t_;
0629 };
0630 
0631 
0632 //-------------------------------------------------------------------
0633 /**
0634  * @brief specialization for vectors: append element
0635  */
0636 template<class T>
0637 class map_arg_to<std::vector<T>>
0638 {
0639 public:
0640     map_arg_to(std::vector<T>& target): t_{std::addressof(target)} {}
0641 
0642     void operator () (const char* s) const {
0643         if(t_ && s) t_->push_back(detail::make<T>::from(s));
0644     }
0645 
0646 private:
0647     std::vector<T>* t_;
0648 };
0649 
0650 
0651 //-------------------------------------------------------------------
0652 /**
0653  * @brief specialization for bools:
0654  *        set to true regardless of string content
0655  */
0656 template<>
0657 class map_arg_to<bool>
0658 {
0659 public:
0660     map_arg_to(bool& target): t_{&target} {}
0661 
0662     void operator () (const char* s) const {
0663         if(t_ && s) *t_ = true;
0664     }
0665 
0666 private:
0667     bool* t_;
0668 };
0669 
0670 
0671 } // namespace detail
0672 
0673 
0674 
0675 
0676 
0677 
0678 /*************************************************************************//**
0679  *
0680  * @brief string matching and processing tools
0681  *
0682  *****************************************************************************/
0683 
0684 namespace str {
0685 
0686 
0687 /*************************************************************************//**
0688  *
0689  * @brief converts string to value of target type 'T'
0690  *
0691  *****************************************************************************/
0692 template<class T>
0693 T make(const arg_string& s)
0694 {
0695     return detail::make<T>::from(s);
0696 }
0697 
0698 
0699 
0700 /*************************************************************************//**
0701  *
0702  * @brief removes trailing whitespace from string
0703  *
0704  *****************************************************************************/
0705 template<class C, class T, class A>
0706 inline void
0707 trimr(std::basic_string<C,T,A>& s)
0708 {
0709     if(s.empty()) return;
0710 
0711     s.erase(
0712         std::find_if_not(s.rbegin(), s.rend(),
0713                          [](char c) { return std::isspace(c);} ).base(),
0714         s.end() );
0715 }
0716 
0717 
0718 /*************************************************************************//**
0719  *
0720  * @brief removes leading whitespace from string
0721  *
0722  *****************************************************************************/
0723 template<class C, class T, class A>
0724 inline void
0725 triml(std::basic_string<C,T,A>& s)
0726 {
0727     if(s.empty()) return;
0728 
0729     s.erase(
0730         s.begin(),
0731         std::find_if_not(s.begin(), s.end(),
0732                          [](char c) { return std::isspace(c);})
0733     );
0734 }
0735 
0736 
0737 /*************************************************************************//**
0738  *
0739  * @brief removes leading and trailing whitespace from string
0740  *
0741  *****************************************************************************/
0742 template<class C, class T, class A>
0743 inline void
0744 trim(std::basic_string<C,T,A>& s)
0745 {
0746     triml(s);
0747     trimr(s);
0748 }
0749 
0750 
0751 /*************************************************************************//**
0752  *
0753  * @brief removes all whitespaces from string
0754  *
0755  *****************************************************************************/
0756 template<class C, class T, class A>
0757 inline void
0758 remove_ws(std::basic_string<C,T,A>& s)
0759 {
0760     if(s.empty()) return;
0761 
0762     s.erase(std::remove_if(s.begin(), s.end(),
0763                            [](char c) { return std::isspace(c); }),
0764             s.end() );
0765 }
0766 
0767 
0768 /*************************************************************************//**
0769  *
0770  * @brief returns true, if the 'prefix' argument
0771  *        is a prefix of the 'subject' argument
0772  *
0773  *****************************************************************************/
0774 template<class C, class T, class A>
0775 inline bool
0776 has_prefix(const std::basic_string<C,T,A>& subject,
0777            const std::basic_string<C,T,A>& prefix)
0778 {
0779     if(prefix.size() > subject.size()) return false;
0780     return subject.find(prefix) == 0;
0781 }
0782 
0783 
0784 /*************************************************************************//**
0785  *
0786  * @brief returns true, if the 'postfix' argument
0787  *        is a postfix of the 'subject' argument
0788  *
0789  *****************************************************************************/
0790 template<class C, class T, class A>
0791 inline bool
0792 has_postfix(const std::basic_string<C,T,A>& subject,
0793             const std::basic_string<C,T,A>& postfix)
0794 {
0795     if(postfix.size() > subject.size()) return false;
0796     return (subject.size() - postfix.size()) == subject.find(postfix);
0797 }
0798 
0799 
0800 
0801 /*************************************************************************//**
0802 *
0803 * @brief   returns longest common prefix of several
0804 *          sequential random access containers
0805 *
0806 * @details InputRange require begin and end (member functions or overloads)
0807 *          the elements of InputRange require a size() member
0808 *
0809 *****************************************************************************/
0810 template<class InputRange>
0811 auto
0812 longest_common_prefix(const InputRange& strs)
0813     -> typename std::decay<decltype(*begin(strs))>::type
0814 {
0815     static_assert(traits::is_input_range<InputRange>(),
0816         "parameter must satisfy the InputRange concept");
0817 
0818     static_assert(traits::has_size_getter<
0819         typename std::decay<decltype(*begin(strs))>::type>(),
0820         "elements of input range must have a ::size() member function");
0821 
0822     using std::begin;
0823     using std::end;
0824 
0825     using item_t = typename std::decay<decltype(*begin(strs))>::type;
0826     using str_size_t = typename std::decay<decltype(begin(strs)->size())>::type;
0827 
0828     const auto n = size_t(distance(begin(strs), end(strs)));
0829     if(n < 1) return item_t("");
0830     if(n == 1) return *begin(strs);
0831 
0832     //length of shortest string
0833     auto m = std::min_element(begin(strs), end(strs),
0834                 [](const item_t& a, const item_t& b) {
0835                     return a.size() < b.size(); })->size();
0836 
0837     //check each character until we find a mismatch
0838     for(str_size_t i = 0; i < m; ++i) {
0839         for(str_size_t j = 1; j < n; ++j) {
0840             if(strs[j][i] != strs[j-1][i])
0841                 return strs[0].substr(0, i);
0842         }
0843     }
0844     return strs[0].substr(0, m);
0845 }
0846 
0847 
0848 
0849 /*************************************************************************//**
0850  *
0851  * @brief  returns longest substring range that could be found in 'arg'
0852  *
0853  * @param  arg         string to be searched in
0854  * @param  substrings  range of candidate substrings
0855  *
0856  *****************************************************************************/
0857 template<class C, class T, class A, class InputRange>
0858 subrange
0859 longest_substring_match(const std::basic_string<C,T,A>& arg,
0860                         const InputRange& substrings)
0861 {
0862     using string_t = std::basic_string<C,T,A>;
0863 
0864     static_assert(traits::is_input_range<InputRange>(),
0865         "parameter must satisfy the InputRange concept");
0866 
0867     static_assert(std::is_same<string_t,
0868         typename std::decay<decltype(*begin(substrings))>::type>(),
0869         "substrings must have same type as 'arg'");
0870 
0871     auto i = string_t::npos;
0872     auto n = string_t::size_type(0);
0873     for(const auto& s : substrings) {
0874         auto j = arg.find(s);
0875         if(j != string_t::npos && s.size() > n) {
0876             i = j;
0877             n = s.size();
0878         }
0879     }
0880     return subrange{i,n};
0881 }
0882 
0883 
0884 
0885 /*************************************************************************//**
0886  *
0887  * @brief  returns longest prefix range that could be found in 'arg'
0888  *
0889  * @param  arg       string to be searched in
0890  * @param  prefixes  range of candidate prefix strings
0891  *
0892  *****************************************************************************/
0893 template<class C, class T, class A, class InputRange>
0894 subrange
0895 longest_prefix_match(const std::basic_string<C,T,A>& arg,
0896                      const InputRange& prefixes)
0897 {
0898     using string_t = std::basic_string<C,T,A>;
0899     using s_size_t = typename string_t::size_type;
0900 
0901     static_assert(traits::is_input_range<InputRange>(),
0902         "parameter must satisfy the InputRange concept");
0903 
0904     static_assert(std::is_same<string_t,
0905         typename std::decay<decltype(*begin(prefixes))>::type>(),
0906         "prefixes must have same type as 'arg'");
0907 
0908     auto i = string_t::npos;
0909     auto n = s_size_t(0);
0910     for(const auto& s : prefixes) {
0911         auto j = arg.find(s);
0912         if(j == 0 && s.size() > n) {
0913             i = 0;
0914             n = s.size();
0915         }
0916     }
0917     return subrange{i,n};
0918 }
0919 
0920 
0921 
0922 /*************************************************************************//**
0923  *
0924  * @brief returns the first occurrence of 'query' within 'subject'
0925  *
0926  *****************************************************************************/
0927 template<class C, class T, class A>
0928 inline subrange
0929 substring_match(const std::basic_string<C,T,A>& subject,
0930                 const std::basic_string<C,T,A>& query)
0931 {
0932     if(subject.empty() && query.empty()) return subrange(0,0);
0933     if(subject.empty() || query.empty()) return subrange{};
0934     auto i = subject.find(query);
0935     if(i == std::basic_string<C,T,A>::npos) return subrange{};
0936     return subrange{i,query.size()};
0937 }
0938 
0939 
0940 
0941 /*************************************************************************//**
0942  *
0943  * @brief returns first substring match (pos,len) within the input string
0944  *        that represents a number
0945  *        (with at maximum one decimal point and digit separators)
0946  *
0947  *****************************************************************************/
0948 template<class C, class T, class A>
0949 subrange
0950 first_number_match(std::basic_string<C,T,A> s,
0951                    C digitSeparator = C(','),
0952                    C decimalPoint = C('.'),
0953                    C exponential = C('e'))
0954 {
0955     using string_t = std::basic_string<C,T,A>;
0956 
0957     str::trim(s);
0958     if(s.empty()) return subrange{};
0959 
0960     auto i = s.find_first_of("0123456789+-");
0961     if(i == string_t::npos) {
0962         i = s.find(decimalPoint);
0963         if(i == string_t::npos) return subrange{};
0964     }
0965 
0966     bool point = false;
0967     bool sep = false;
0968     auto exp = string_t::npos;
0969     auto j = i + 1;
0970     for(; j < s.size(); ++j) {
0971         if(s[j] == digitSeparator) {
0972             if(!sep) sep = true; else break;
0973         }
0974         else {
0975             sep = false;
0976             if(s[j] == decimalPoint) {
0977                 //only one decimal point before exponent allowed
0978                 if(!point && exp == string_t::npos) point = true; else break;
0979             }
0980             else if(std::tolower(s[j]) == std::tolower(exponential)) {
0981                 //only one exponent separator allowed
0982                 if(exp == string_t::npos) exp = j; else break;
0983             }
0984             else if(exp != string_t::npos && (exp+1) == j) {
0985                 //only sign or digit after exponent separator
0986                 if(s[j] != '+' && s[j] != '-' && !std::isdigit(s[j])) break;
0987             }
0988             else if(!std::isdigit(s[j])) {
0989                 break;
0990             }
0991         }
0992     }
0993 
0994     //if length == 1 then must be a digit
0995     if(j-i == 1 && !std::isdigit(s[i])) return subrange{};
0996 
0997     return subrange{i,j-i};
0998 }
0999 
1000 
1001 
1002 /*************************************************************************//**
1003  *
1004  * @brief returns first substring match (pos,len)
1005  *        that represents an integer (with optional digit separators)
1006  *
1007  *****************************************************************************/
1008 template<class C, class T, class A>
1009 subrange
1010 first_integer_match(std::basic_string<C,T,A> s,
1011                     C digitSeparator = C(','))
1012 {
1013     using string_t = std::basic_string<C,T,A>;
1014 
1015     str::trim(s);
1016     if(s.empty()) return subrange{};
1017 
1018     auto i = s.find_first_of("0123456789+-");
1019     if(i == string_t::npos) return subrange{};
1020 
1021     bool sep = false;
1022     auto j = i + 1;
1023     for(; j < s.size(); ++j) {
1024         if(s[j] == digitSeparator) {
1025             if(!sep) sep = true; else break;
1026         }
1027         else {
1028             sep = false;
1029             if(!std::isdigit(s[j])) break;
1030         }
1031     }
1032 
1033     //if length == 1 then must be a digit
1034     if(j-i == 1 && !std::isdigit(s[i])) return subrange{};
1035 
1036     return subrange{i,j-i};
1037 }
1038 
1039 
1040 
1041 /*************************************************************************//**
1042  *
1043  * @brief returns true if candidate string represents a number
1044  *
1045  *****************************************************************************/
1046 template<class C, class T, class A>
1047 bool represents_number(const std::basic_string<C,T,A>& candidate,
1048                    C digitSeparator = C(','),
1049                    C decimalPoint = C('.'),
1050                    C exponential = C('e'))
1051 {
1052     const auto match = str::first_number_match(candidate, digitSeparator,
1053                                                decimalPoint, exponential);
1054 
1055     return (match && match.length() == candidate.size());
1056 }
1057 
1058 
1059 
1060 /*************************************************************************//**
1061  *
1062  * @brief returns true if candidate string represents an integer
1063  *
1064  *****************************************************************************/
1065 template<class C, class T, class A>
1066 bool represents_integer(const std::basic_string<C,T,A>& candidate,
1067                         C digitSeparator = C(','))
1068 {
1069     const auto match = str::first_integer_match(candidate, digitSeparator);
1070     return (match && match.length() == candidate.size());
1071 }
1072 
1073 } // namespace str
1074 
1075 
1076 
1077 
1078 
1079 
1080 /*************************************************************************//**
1081  *
1082  * @brief makes function object with a const char* parameter
1083  *        that assigns a value to a ref-captured object
1084  *
1085  *****************************************************************************/
1086 template<class T, class V>
1087 inline detail::assign_value<T,V>
1088 set(T& target, V value) {
1089     return detail::assign_value<T>{target, std::move(value)};
1090 }
1091 
1092 
1093 
1094 /*************************************************************************//**
1095  *
1096  * @brief makes parameter-less function object
1097  *        that assigns value(s) to a ref-captured object;
1098  *        value(s) are obtained by converting the const char* argument to
1099  *        the captured object types;
1100  *        bools are always set to true if the argument is not nullptr
1101  *
1102  *****************************************************************************/
1103 template<class T>
1104 inline detail::map_arg_to<T>
1105 set(T& target) {
1106     return detail::map_arg_to<T>{target};
1107 }
1108 
1109 
1110 
1111 /*************************************************************************//**
1112  *
1113  * @brief makes function object that sets a bool to true
1114  *
1115  *****************************************************************************/
1116 inline detail::assign_value<bool>
1117 set(bool& target) {
1118     return detail::assign_value<bool>{target,true};
1119 }
1120 
1121 /*************************************************************************//**
1122  *
1123  * @brief makes function object that sets a bool to false
1124  *
1125  *****************************************************************************/
1126 inline detail::assign_value<bool>
1127 unset(bool& target) {
1128     return detail::assign_value<bool>{target,false};
1129 }
1130 
1131 /*************************************************************************//**
1132  *
1133  * @brief makes function object that flips the value of a ref-captured bool
1134  *
1135  *****************************************************************************/
1136 inline detail::flip_bool
1137 flip(bool& b) {
1138     return detail::flip_bool(b);
1139 }
1140 
1141 
1142 
1143 
1144 
1145 /*************************************************************************//**
1146  *
1147  * @brief makes function object that increments using operator ++
1148  *
1149  *****************************************************************************/
1150 template<class T>
1151 inline detail::increment<T>
1152 increment(T& target) {
1153     return detail::increment<T>{target};
1154 }
1155 
1156 /*************************************************************************//**
1157  *
1158  * @brief makes function object that decrements using operator --
1159  *
1160  *****************************************************************************/
1161 template<class T>
1162 inline detail::increment_by<T>
1163 increment(T& target, T by) {
1164     return detail::increment_by<T>{target, std::move(by)};
1165 }
1166 
1167 /*************************************************************************//**
1168  *
1169  * @brief makes function object that increments by a fixed amount using operator +=
1170  *
1171  *****************************************************************************/
1172 template<class T>
1173 inline detail::decrement<T>
1174 decrement(T& target) {
1175     return detail::decrement<T>{target};
1176 }
1177 
1178 
1179 
1180 
1181 
1182 
1183 /*************************************************************************//**
1184  *
1185  * @brief helpers (NOT FOR DIRECT USE IN CLIENT CODE!)
1186  *
1187  *****************************************************************************/
1188 namespace detail {
1189 
1190 
1191 /*************************************************************************//**
1192  *
1193  * @brief mixin that provides action definition and execution
1194  *
1195  *****************************************************************************/
1196 template<class Derived>
1197 class action_provider
1198 {
1199 private:
1200     //---------------------------------------------------------------
1201     using simple_action = std::function<void()>;
1202     using arg_action    = std::function<void(const char*)>;
1203     using index_action  = std::function<void(int)>;
1204 
1205     //-----------------------------------------------------
1206     class simple_action_adapter {
1207     public:
1208         simple_action_adapter() = default;
1209         simple_action_adapter(const simple_action& a): action_(a) {}
1210         simple_action_adapter(simple_action&& a): action_(std::move(a)) {}
1211         void operator() (const char*) const { action_(); }
1212         void operator() (int) const { action_(); }
1213     private:
1214         simple_action action_;
1215     };
1216 
1217 
1218 public:
1219     //---------------------------------------------------------------
1220     /** @brief adds an action that has an operator() that is callable
1221      *         with a 'const char*' argument */
1222     Derived&
1223     call(arg_action a) {
1224         argActions_.push_back(std::move(a));
1225         return *static_cast<Derived*>(this);
1226     }
1227 
1228     /** @brief adds an action that has an operator()() */
1229     Derived&
1230     call(simple_action a) {
1231         argActions_.push_back(simple_action_adapter(std::move(a)));
1232         return *static_cast<Derived*>(this);
1233     }
1234 
1235     /** @brief adds an action that has an operator() that is callable
1236      *         with a 'const char*' argument */
1237     Derived& operator () (arg_action a)    { return call(std::move(a)); }
1238 
1239     /** @brief adds an action that has an operator()() */
1240     Derived& operator () (simple_action a) { return call(std::move(a)); }
1241 
1242 
1243     //---------------------------------------------------------------
1244     /** @brief adds an action that will set the value of 't' from
1245      *         a 'const char*' arg */
1246     template<class Target>
1247     Derived&
1248     set(Target& t) {
1249         static_assert(!std::is_pointer<Target>::value,
1250                       "parameter target type must not be a pointer");
1251 
1252         return call(clipp::set(t));
1253     }
1254 
1255     /** @brief adds an action that will set the value of 't' to 'v' */
1256     template<class Target, class Value>
1257     Derived&
1258     set(Target& t, Value&& v) {
1259         return call(clipp::set(t, std::forward<Value>(v)));
1260     }
1261 
1262 
1263     //---------------------------------------------------------------
1264     /** @brief adds an action that will be called if a parameter
1265      *         matches an argument for the 2nd, 3rd, 4th, ... time
1266      */
1267     Derived&
1268     if_repeated(simple_action a) {
1269         repeatActions_.push_back(simple_action_adapter{std::move(a)});
1270         return *static_cast<Derived*>(this);
1271     }
1272     /** @brief adds an action that will be called with the argument's
1273      *         index if a parameter matches an argument for
1274      *         the 2nd, 3rd, 4th, ... time
1275      */
1276     Derived&
1277     if_repeated(index_action a) {
1278         repeatActions_.push_back(std::move(a));
1279         return *static_cast<Derived*>(this);
1280     }
1281 
1282 
1283     //---------------------------------------------------------------
1284     /** @brief adds an action that will be called if a required parameter
1285      *         is missing
1286      */
1287     Derived&
1288     if_missing(simple_action a) {
1289         missingActions_.push_back(simple_action_adapter{std::move(a)});
1290         return *static_cast<Derived*>(this);
1291     }
1292     /** @brief adds an action that will be called if a required parameter
1293      *         is missing; the action will get called with the index of
1294      *         the command line argument where the missing event occurred first
1295      */
1296     Derived&
1297     if_missing(index_action a) {
1298         missingActions_.push_back(std::move(a));
1299         return *static_cast<Derived*>(this);
1300     }
1301 
1302 
1303     //---------------------------------------------------------------
1304     /** @brief adds an action that will be called if a parameter
1305      *         was matched, but was unreachable in the current scope
1306      */
1307     Derived&
1308     if_blocked(simple_action a) {
1309         blockedActions_.push_back(simple_action_adapter{std::move(a)});
1310         return *static_cast<Derived*>(this);
1311     }
1312     /** @brief adds an action that will be called if a parameter
1313      *         was matched, but was unreachable in the current scope;
1314      *         the action will be called with the index of
1315      *         the command line argument where the problem occurred
1316      */
1317     Derived&
1318     if_blocked(index_action a) {
1319         blockedActions_.push_back(std::move(a));
1320         return *static_cast<Derived*>(this);
1321     }
1322 
1323 
1324     //---------------------------------------------------------------
1325     /** @brief adds an action that will be called if a parameter match
1326      *         was in conflict with a different alternative parameter
1327      */
1328     Derived&
1329     if_conflicted(simple_action a) {
1330         conflictActions_.push_back(simple_action_adapter{std::move(a)});
1331         return *static_cast<Derived*>(this);
1332     }
1333     /** @brief adds an action that will be called if a parameter match
1334      *         was in conflict with a different alternative parameter;
1335      *         the action will be called with the index of
1336      *         the command line argument where the problem occurred
1337      */
1338     Derived&
1339     if_conflicted(index_action a) {
1340         conflictActions_.push_back(std::move(a));
1341         return *static_cast<Derived*>(this);
1342     }
1343 
1344 
1345     //---------------------------------------------------------------
1346     /** @brief adds targets = either objects whose values should be
1347      *         set by command line arguments or actions that should
1348      *         be called in case of a match */
1349     template<class T, class... Ts>
1350     Derived&
1351     target(T&& t, Ts&&... ts) {
1352         target(std::forward<T>(t));
1353         target(std::forward<Ts>(ts)...);
1354         return *static_cast<Derived*>(this);
1355     }
1356 
1357     /** @brief adds action that should be called in case of a match */
1358     template<class T, class = typename std::enable_if<
1359             !std::is_fundamental<typename std::decay<T>::type>() &&
1360             (traits::is_callable<T,void()>() ||
1361              traits::is_callable<T,void(const char*)>() )
1362         >::type>
1363     Derived&
1364     target(T&& t) {
1365         call(std::forward<T>(t));
1366         return *static_cast<Derived*>(this);
1367     }
1368 
1369     /** @brief adds object whose value should be set by command line arguments
1370      */
1371     template<class T, class = typename std::enable_if<
1372             std::is_fundamental<typename std::decay<T>::type>() ||
1373             (!traits::is_callable<T,void()>() &&
1374              !traits::is_callable<T,void(const char*)>() )
1375         >::type>
1376     Derived&
1377     target(T& t) {
1378         set(t);
1379         return *static_cast<Derived*>(this);
1380     }
1381 
1382     //TODO remove ugly empty param list overload
1383     Derived&
1384     target() {
1385         return *static_cast<Derived*>(this);
1386     }
1387 
1388 
1389     //---------------------------------------------------------------
1390     /** @brief adds target, see member function 'target' */
1391     template<class Target>
1392     inline friend Derived&
1393     operator << (Target&& t, Derived& p) {
1394         p.target(std::forward<Target>(t));
1395         return p;
1396     }
1397     /** @brief adds target, see member function 'target' */
1398     template<class Target>
1399     inline friend Derived&&
1400     operator << (Target&& t, Derived&& p) {
1401         p.target(std::forward<Target>(t));
1402         return std::move(p);
1403     }
1404 
1405     //-----------------------------------------------------
1406     /** @brief adds target, see member function 'target' */
1407     template<class Target>
1408     inline friend Derived&
1409     operator >> (Derived& p, Target&& t) {
1410         p.target(std::forward<Target>(t));
1411         return p;
1412     }
1413     /** @brief adds target, see member function 'target' */
1414     template<class Target>
1415     inline friend Derived&&
1416     operator >> (Derived&& p, Target&& t) {
1417         p.target(std::forward<Target>(t));
1418         return std::move(p);
1419     }
1420 
1421 
1422     //---------------------------------------------------------------
1423     /** @brief executes all argument actions */
1424     void execute_actions(const arg_string& arg) const {
1425         for(const auto& a : argActions_) {
1426             a(arg.c_str());
1427         }
1428     }
1429 
1430     /** @brief executes repeat actions */
1431     void notify_repeated(arg_index idx) const {
1432         for(const auto& a : repeatActions_) a(idx);
1433     }
1434     /** @brief executes missing error actions */
1435     void notify_missing(arg_index idx) const {
1436         for(const auto& a : missingActions_) a(idx);
1437     }
1438     /** @brief executes blocked error actions */
1439     void notify_blocked(arg_index idx) const {
1440         for(const auto& a : blockedActions_) a(idx);
1441     }
1442     /** @brief executes conflict error actions */
1443     void notify_conflict(arg_index idx) const {
1444         for(const auto& a : conflictActions_) a(idx);
1445     }
1446 
1447 private:
1448     //---------------------------------------------------------------
1449     std::vector<arg_action> argActions_;
1450     std::vector<index_action> repeatActions_;
1451     std::vector<index_action> missingActions_;
1452     std::vector<index_action> blockedActions_;
1453     std::vector<index_action> conflictActions_;
1454 };
1455 
1456 
1457 
1458 
1459 
1460 
1461 /*************************************************************************//**
1462  *
1463  * @brief mixin that provides basic common settings of parameters and groups
1464  *
1465  *****************************************************************************/
1466 template<class Derived>
1467 class token
1468 {
1469 public:
1470     //---------------------------------------------------------------
1471     using doc_string = clipp::doc_string;
1472 
1473 
1474     //---------------------------------------------------------------
1475     /** @brief returns documentation string */
1476     const doc_string& doc() const noexcept {
1477         return doc_;
1478     }
1479 
1480     /** @brief sets documentations string */
1481     Derived& doc(const doc_string& txt) {
1482         doc_ = txt;
1483         return *static_cast<Derived*>(this);
1484     }
1485 
1486     /** @brief sets documentations string */
1487     Derived& doc(doc_string&& txt) {
1488         doc_ = std::move(txt);
1489         return *static_cast<Derived*>(this);
1490     }
1491 
1492 
1493     //---------------------------------------------------------------
1494     /** @brief returns if a group/parameter is repeatable */
1495     bool repeatable() const noexcept {
1496         return repeatable_;
1497     }
1498 
1499     /** @brief sets repeatability of group/parameter */
1500     Derived& repeatable(bool yes) noexcept {
1501         repeatable_ = yes;
1502         return *static_cast<Derived*>(this);
1503     }
1504 
1505 
1506     //---------------------------------------------------------------
1507     /** @brief returns if a group/parameter is blocking/positional */
1508     bool blocking() const noexcept {
1509         return blocking_;
1510     }
1511 
1512     /** @brief determines, if a group/parameter is blocking/positional */
1513     Derived& blocking(bool yes) noexcept {
1514         blocking_ = yes;
1515         return *static_cast<Derived*>(this);
1516     }
1517 
1518 
1519 private:
1520     //---------------------------------------------------------------
1521     doc_string doc_;
1522     bool repeatable_ = false;
1523     bool blocking_ = false;
1524 };
1525 
1526 
1527 
1528 
1529 /*************************************************************************//**
1530  *
1531  * @brief sets documentation strings on a token
1532  *
1533  *****************************************************************************/
1534 template<class T>
1535 inline T&
1536 operator % (doc_string docstr, token<T>& p)
1537 {
1538     return p.doc(std::move(docstr));
1539 }
1540 //---------------------------------------------------------
1541 template<class T>
1542 inline T&&
1543 operator % (doc_string docstr, token<T>&& p)
1544 {
1545     return std::move(p.doc(std::move(docstr)));
1546 }
1547 
1548 //---------------------------------------------------------
1549 template<class T>
1550 inline T&
1551 operator % (token<T>& p, doc_string docstr)
1552 {
1553     return p.doc(std::move(docstr));
1554 }
1555 //---------------------------------------------------------
1556 template<class T>
1557 inline T&&
1558 operator % (token<T>&& p, doc_string docstr)
1559 {
1560     return std::move(p.doc(std::move(docstr)));
1561 }
1562 
1563 
1564 
1565 
1566 /*************************************************************************//**
1567  *
1568  * @brief sets documentation strings on a token
1569  *
1570  *****************************************************************************/
1571 template<class T>
1572 inline T&
1573 doc(doc_string docstr, token<T>& p)
1574 {
1575     return p.doc(std::move(docstr));
1576 }
1577 //---------------------------------------------------------
1578 template<class T>
1579 inline T&&
1580 doc(doc_string docstr, token<T>&& p)
1581 {
1582     return std::move(p.doc(std::move(docstr)));
1583 }
1584 
1585 
1586 
1587 } // namespace detail
1588 
1589 
1590 
1591 /*************************************************************************//**
1592  *
1593  * @brief contains parameter matching functions and function classes
1594  *
1595  *****************************************************************************/
1596 namespace match {
1597 
1598 
1599 /*************************************************************************//**
1600  *
1601  * @brief predicate that is always true
1602  *
1603  *****************************************************************************/
1604 inline bool
1605 any(const arg_string&) { return true; }
1606 
1607 /*************************************************************************//**
1608  *
1609  * @brief predicate that is always false
1610  *
1611  *****************************************************************************/
1612 inline bool
1613 none(const arg_string&) { return false; }
1614 
1615 
1616 
1617 /*************************************************************************//**
1618  *
1619  * @brief predicate that returns true if the argument string is non-empty string
1620  *
1621  *****************************************************************************/
1622 inline bool
1623 nonempty(const arg_string& s) {
1624     return !s.empty();
1625 }
1626 
1627 
1628 
1629 /*************************************************************************//**
1630  *
1631  * @brief predicate that returns true if the argument is a non-empty
1632  *        string that consists only of alphanumeric characters
1633  *
1634  *****************************************************************************/
1635 inline bool
1636 alphanumeric(const arg_string& s) {
1637     if(s.empty()) return false;
1638     return std::all_of(s.begin(), s.end(), [](char c) {return std::isalnum(c); });
1639 }
1640 
1641 
1642 
1643 /*************************************************************************//**
1644  *
1645  * @brief predicate that returns true if the argument is a non-empty
1646  *        string that consists only of alphabetic characters
1647  *
1648  *****************************************************************************/
1649 inline bool
1650 alphabetic(const arg_string& s) {
1651     return std::all_of(s.begin(), s.end(), [](char c) {return std::isalpha(c); });
1652 }
1653 
1654 
1655 
1656 /*************************************************************************//**
1657  *
1658  * @brief predicate that returns false if the argument string is
1659  *        equal to any string from the exclusion list
1660  *
1661  *****************************************************************************/
1662 class none_of
1663 {
1664 public:
1665     none_of(arg_list strs):
1666         excluded_{std::move(strs)}
1667     {}
1668 
1669     template<class... Strings>
1670     none_of(arg_string str, Strings&&... strs):
1671         excluded_{std::move(str), std::forward<Strings>(strs)...}
1672     {}
1673 
1674     template<class... Strings>
1675     none_of(const char* str, Strings&&... strs):
1676         excluded_{arg_string(str), std::forward<Strings>(strs)...}
1677     {}
1678 
1679     bool operator () (const arg_string& arg) const {
1680         return (std::find(begin(excluded_), end(excluded_), arg)
1681                 == end(excluded_));
1682     }
1683 
1684 private:
1685     arg_list excluded_;
1686 };
1687 
1688 
1689 
1690 /*************************************************************************//**
1691  *
1692  * @brief predicate that returns the first substring match within the input
1693  *        string that rmeepresents a number
1694  *        (with at maximum one decimal point and digit separators)
1695  *
1696  *****************************************************************************/
1697 class numbers
1698 {
1699 public:
1700     explicit
1701     numbers(char decimalPoint = '.',
1702             char digitSeparator = ' ',
1703             char exponentSeparator = 'e')
1704     :
1705         decpoint_{decimalPoint}, separator_{digitSeparator},
1706         exp_{exponentSeparator}
1707     {}
1708 
1709     subrange operator () (const arg_string& s) const {
1710         return str::first_number_match(s, separator_, decpoint_, exp_);
1711     }
1712 
1713 private:
1714     char decpoint_;
1715     char separator_;
1716     char exp_;
1717 };
1718 
1719 
1720 
1721 /*************************************************************************//**
1722  *
1723  * @brief predicate that returns true if the input string represents an integer
1724  *        (with optional digit separators)
1725  *
1726  *****************************************************************************/
1727 class integers {
1728 public:
1729     explicit
1730     integers(char digitSeparator = ' '): separator_{digitSeparator} {}
1731 
1732     subrange operator () (const arg_string& s) const {
1733         return str::first_integer_match(s, separator_);
1734     }
1735 
1736 private:
1737     char separator_;
1738 };
1739 
1740 
1741 
1742 /*************************************************************************//**
1743  *
1744  * @brief predicate that returns true if the input string represents
1745  *        a non-negative integer (with optional digit separators)
1746  *
1747  *****************************************************************************/
1748 class positive_integers {
1749 public:
1750     explicit
1751     positive_integers(char digitSeparator = ' '): separator_{digitSeparator} {}
1752 
1753     subrange operator () (const arg_string& s) const {
1754         auto match = str::first_integer_match(s, separator_);
1755         if(!match) return subrange{};
1756         if(s[match.at()] == '-') return subrange{};
1757         return match;
1758     }
1759 
1760 private:
1761     char separator_;
1762 };
1763 
1764 
1765 
1766 /*************************************************************************//**
1767  *
1768  * @brief predicate that returns true if the input string
1769  *        contains a given substring
1770  *
1771  *****************************************************************************/
1772 class substring
1773 {
1774 public:
1775     explicit
1776     substring(arg_string str): str_{std::move(str)} {}
1777 
1778     subrange operator () (const arg_string& s) const {
1779         return str::substring_match(s, str_);
1780     }
1781 
1782 private:
1783     arg_string str_;
1784 };
1785 
1786 
1787 
1788 /*************************************************************************//**
1789  *
1790  * @brief predicate that returns true if the input string starts
1791  *        with a given prefix
1792  *
1793  *****************************************************************************/
1794 class prefix {
1795 public:
1796     explicit
1797     prefix(arg_string p): prefix_{std::move(p)} {}
1798 
1799     bool operator () (const arg_string& s) const {
1800         return s.find(prefix_) == 0;
1801     }
1802 
1803 private:
1804     arg_string prefix_;
1805 };
1806 
1807 
1808 
1809 /*************************************************************************//**
1810  *
1811  * @brief predicate that returns true if the input string does not start
1812  *        with a given prefix
1813  *
1814  *****************************************************************************/
1815 class prefix_not {
1816 public:
1817     explicit
1818     prefix_not(arg_string p): prefix_{std::move(p)} {}
1819 
1820     bool operator () (const arg_string& s) const {
1821         return s.find(prefix_) != 0;
1822     }
1823 
1824 private:
1825     arg_string prefix_;
1826 };
1827 
1828 
1829 /** @brief alias for prefix_not */
1830 using noprefix = prefix_not;
1831 
1832 
1833 
1834 /*************************************************************************//**
1835  *
1836  * @brief predicate that returns true if the length of the input string
1837  *        is wihtin a given interval
1838  *
1839  *****************************************************************************/
1840 class length {
1841 public:
1842     explicit
1843     length(std::size_t exact):
1844         min_{exact}, max_{exact}
1845     {}
1846 
1847     explicit
1848     length(std::size_t min, std::size_t max):
1849         min_{min}, max_{max}
1850     {}
1851 
1852     bool operator () (const arg_string& s) const {
1853         return s.size() >= min_ && s.size() <= max_;
1854     }
1855 
1856 private:
1857     std::size_t min_;
1858     std::size_t max_;
1859 };
1860 
1861 
1862 /*************************************************************************//**
1863  *
1864  * @brief makes function object that returns true if the input string has a
1865  *        given minimum length
1866  *
1867  *****************************************************************************/
1868 inline length min_length(std::size_t min)
1869 {
1870     return length{min, arg_string::npos-1};
1871 }
1872 
1873 /*************************************************************************//**
1874  *
1875  * @brief makes function object that returns true if the input string is
1876  *        not longer than a given maximum length
1877  *
1878  *****************************************************************************/
1879 inline length max_length(std::size_t max)
1880 {
1881     return length{0, max};
1882 }
1883 
1884 
1885 } // namespace match
1886 
1887 
1888 
1889 
1890 
1891 /*************************************************************************//**
1892  *
1893  * @brief command line parameter that can match one or many arguments.
1894  *
1895  *****************************************************************************/
1896 class parameter :
1897     public detail::token<parameter>,
1898     public detail::action_provider<parameter>
1899 {
1900     /** @brief adapts a 'match_predicate' to the 'match_function' interface */
1901     class predicate_adapter {
1902     public:
1903         explicit
1904         predicate_adapter(match_predicate pred): match_{std::move(pred)} {}
1905 
1906         subrange operator () (const arg_string& arg) const {
1907             return match_(arg) ? subrange{0,arg.size()} : subrange{};
1908         }
1909 
1910     private:
1911         match_predicate match_;
1912     };
1913 
1914 public:
1915     //---------------------------------------------------------------
1916     /** @brief makes default parameter, that will match nothing */
1917     parameter():
1918         flags_{},
1919         matcher_{predicate_adapter{match::none}},
1920         label_{}, required_{false}, greedy_{false}
1921     {}
1922 
1923     /** @brief makes "flag" parameter */
1924     template<class... Strings>
1925     explicit
1926     parameter(arg_string str, Strings&&... strs):
1927         flags_{},
1928         matcher_{predicate_adapter{match::none}},
1929         label_{}, required_{false}, greedy_{false}
1930     {
1931         add_flags(std::move(str), std::forward<Strings>(strs)...);
1932     }
1933 
1934     /** @brief makes "flag" parameter from range of strings */
1935     explicit
1936     parameter(const arg_list& flaglist):
1937         flags_{},
1938         matcher_{predicate_adapter{match::none}},
1939         label_{}, required_{false}, greedy_{false}
1940     {
1941         add_flags(flaglist);
1942     }
1943 
1944     //-----------------------------------------------------
1945     /** @brief makes "value" parameter with custom match predicate
1946      *         (= yes/no matcher)
1947      */
1948     explicit
1949     parameter(match_predicate filter):
1950         flags_{},
1951         matcher_{predicate_adapter{std::move(filter)}},
1952         label_{}, required_{false}, greedy_{false}
1953     {}
1954 
1955     /** @brief makes "value" parameter with custom match function
1956      *         (= partial matcher)
1957      */
1958     explicit
1959     parameter(match_function filter):
1960         flags_{},
1961         matcher_{std::move(filter)},
1962         label_{}, required_{false}, greedy_{false}
1963     {}
1964 
1965 
1966     //---------------------------------------------------------------
1967     /** @brief returns if a parameter is required */
1968     bool
1969     required() const noexcept {
1970         return required_;
1971     }
1972 
1973     /** @brief determines if a parameter is required */
1974     parameter&
1975     required(bool yes) noexcept {
1976         required_ = yes;
1977         return *this;
1978     }
1979 
1980 
1981     //---------------------------------------------------------------
1982     /** @brief returns if a parameter should match greedily */
1983     bool
1984     greedy() const noexcept {
1985         return greedy_;
1986     }
1987 
1988     /** @brief determines if a parameter should match greedily */
1989     parameter&
1990     greedy(bool yes) noexcept {
1991         greedy_ = yes;
1992         return *this;
1993     }
1994 
1995 
1996     //---------------------------------------------------------------
1997     /** @brief returns parameter label;
1998      *         will be used for documentation, if flags are empty
1999      */
2000     const doc_string&
2001     label() const {
2002         return label_;
2003     }
2004 
2005     /** @brief sets parameter label;
2006      *         will be used for documentation, if flags are empty
2007      */
2008     parameter&
2009     label(const doc_string& lbl) {
2010         label_ = lbl;
2011         return *this;
2012     }
2013 
2014     /** @brief sets parameter label;
2015      *         will be used for documentation, if flags are empty
2016      */
2017     parameter&
2018     label(doc_string&& lbl) {
2019         label_ = lbl;
2020         return *this;
2021     }
2022 
2023 
2024     //---------------------------------------------------------------
2025     /** @brief returns either longest matching prefix of 'arg' in any
2026      *         of the flags or the result of the custom match operation
2027      */
2028     subrange
2029     match(const arg_string& arg) const
2030     {
2031         if(flags_.empty()) {
2032             return matcher_(arg);
2033         }
2034         else {
2035             //empty flags are not allowed
2036             if(arg.empty()) return subrange{};
2037 
2038             if(std::find(flags_.begin(), flags_.end(), arg) != flags_.end()) {
2039                 return subrange{0,arg.size()};
2040             }
2041             return str::longest_prefix_match(arg, flags_);
2042         }
2043     }
2044 
2045 
2046     //---------------------------------------------------------------
2047     /** @brief access range of flag strings */
2048     const arg_list&
2049     flags() const noexcept {
2050         return flags_;
2051     }
2052 
2053     /** @brief access custom match operation */
2054     const match_function&
2055     matcher() const noexcept {
2056         return matcher_;
2057     }
2058 
2059 
2060     //---------------------------------------------------------------
2061     /** @brief prepend prefix to each flag */
2062     inline friend parameter&
2063     with_prefix(const arg_string& prefix, parameter& p)
2064     {
2065         if(prefix.empty() || p.flags().empty()) return p;
2066 
2067         for(auto& f : p.flags_) {
2068             if(f.find(prefix) != 0) f.insert(0, prefix);
2069         }
2070         return p;
2071     }
2072 
2073 
2074     /** @brief prepend prefix to each flag
2075      */
2076     inline friend parameter&
2077     with_prefixes_short_long(
2078         const arg_string& shortpfx, const arg_string& longpfx,
2079         parameter& p)
2080     {
2081         if(shortpfx.empty() && longpfx.empty()) return p;
2082         if(p.flags().empty()) return p;
2083 
2084         for(auto& f : p.flags_) {
2085             if(f.size() == 1) {
2086                 if(f.find(shortpfx) != 0) f.insert(0, shortpfx);
2087             } else {
2088                 if(f.find(longpfx) != 0) f.insert(0, longpfx);
2089             }
2090         }
2091         return p;
2092     }
2093 
2094 
2095     //---------------------------------------------------------------
2096     /** @brief prepend suffix to each flag */
2097     inline friend parameter&
2098     with_suffix(const arg_string& suffix, parameter& p)
2099     {
2100         if(suffix.empty() || p.flags().empty()) return p;
2101 
2102         for(auto& f : p.flags_) {
2103             if(f.find(suffix) + suffix.size() != f.size()) {
2104                 f.insert(f.end(), suffix.begin(), suffix.end());
2105             }
2106         }
2107         return p;
2108     }
2109 
2110 
2111     /** @brief prepend suffix to each flag
2112      */
2113     inline friend parameter&
2114     with_suffixes_short_long(
2115         const arg_string& shortsfx, const arg_string& longsfx,
2116         parameter& p)
2117     {
2118         if(shortsfx.empty() && longsfx.empty()) return p;
2119         if(p.flags().empty()) return p;
2120 
2121         for(auto& f : p.flags_) {
2122             if(f.size() == 1) {
2123                 if(f.find(shortsfx) + shortsfx.size() != f.size()) {
2124                     f.insert(f.end(), shortsfx.begin(), shortsfx.end());
2125                 }
2126             } else {
2127                 if(f.find(longsfx) + longsfx.size() != f.size()) {
2128                     f.insert(f.end(), longsfx.begin(), longsfx.end());
2129                 }
2130             }
2131         }
2132         return p;
2133     }
2134 
2135 private:
2136     //---------------------------------------------------------------
2137     void add_flags(arg_string str) {
2138         //empty flags are not allowed
2139         str::remove_ws(str);
2140         if(!str.empty()) flags_.push_back(std::move(str));
2141     }
2142 
2143     //---------------------------------------------------------------
2144     void add_flags(const arg_list& strs) {
2145         if(strs.empty()) return;
2146         flags_.reserve(flags_.size() + strs.size());
2147         for(const auto& s : strs) add_flags(s);
2148     }
2149 
2150     template<class String1, class String2, class... Strings>
2151     void
2152     add_flags(String1&& s1, String2&& s2, Strings&&... ss) {
2153         flags_.reserve(2 + sizeof...(ss));
2154         add_flags(std::forward<String1>(s1));
2155         add_flags(std::forward<String2>(s2), std::forward<Strings>(ss)...);
2156     }
2157 
2158     arg_list flags_;
2159     match_function matcher_;
2160     doc_string label_;
2161     bool required_ = false;
2162     bool greedy_ = false;
2163 };
2164 
2165 
2166 
2167 
2168 /*************************************************************************//**
2169  *
2170  * @brief makes required non-blocking exact match parameter
2171  *
2172  *****************************************************************************/
2173 template<class String, class... Strings>
2174 inline parameter
2175 command(String&& flag, Strings&&... flags)
2176 {
2177     return parameter{std::forward<String>(flag), std::forward<Strings>(flags)...}
2178         .required(true).blocking(true).repeatable(false);
2179 }
2180 
2181 
2182 
2183 /*************************************************************************//**
2184  *
2185  * @brief makes required non-blocking exact match parameter
2186  *
2187  *****************************************************************************/
2188 template<class String, class... Strings>
2189 inline parameter
2190 required(String&& flag, Strings&&... flags)
2191 {
2192     return parameter{std::forward<String>(flag), std::forward<Strings>(flags)...}
2193         .required(true).blocking(false).repeatable(false);
2194 }
2195 
2196 
2197 
2198 /*************************************************************************//**
2199  *
2200  * @brief makes optional, non-blocking exact match parameter
2201  *
2202  *****************************************************************************/
2203 template<class String, class... Strings>
2204 inline parameter
2205 option(String&& flag, Strings&&... flags)
2206 {
2207     return parameter{std::forward<String>(flag), std::forward<Strings>(flags)...}
2208         .required(false).blocking(false).repeatable(false);
2209 }
2210 
2211 
2212 
2213 /*************************************************************************//**
2214  *
2215  * @brief makes required, blocking, repeatable value parameter;
2216  *        matches any non-empty string
2217  *
2218  *****************************************************************************/
2219 template<class... Targets>
2220 inline parameter
2221 value(const doc_string& label, Targets&&... tgts)
2222 {
2223     return parameter{match::nonempty}
2224         .label(label)
2225         .target(std::forward<Targets>(tgts)...)
2226         .required(true).blocking(true).repeatable(false);
2227 }
2228 
2229 template<class Filter, class... Targets, class = typename std::enable_if<
2230     traits::is_callable<Filter,bool(const char*)>::value ||
2231     traits::is_callable<Filter,subrange(const char*)>::value>::type>
2232 inline parameter
2233 value(Filter&& filter, doc_string label, Targets&&... tgts)
2234 {
2235     return parameter{std::forward<Filter>(filter)}
2236         .label(label)
2237         .target(std::forward<Targets>(tgts)...)
2238         .required(true).blocking(true).repeatable(false);
2239 }
2240 
2241 
2242 
2243 /*************************************************************************//**
2244  *
2245  * @brief makes required, blocking, repeatable value parameter;
2246  *        matches any non-empty string
2247  *
2248  *****************************************************************************/
2249 template<class... Targets>
2250 inline parameter
2251 values(const doc_string& label, Targets&&... tgts)
2252 {
2253     return parameter{match::nonempty}
2254         .label(label)
2255         .target(std::forward<Targets>(tgts)...)
2256         .required(true).blocking(true).repeatable(true);
2257 }
2258 
2259 template<class Filter, class... Targets, class = typename std::enable_if<
2260     traits::is_callable<Filter,bool(const char*)>::value ||
2261     traits::is_callable<Filter,subrange(const char*)>::value>::type>
2262 inline parameter
2263 values(Filter&& filter, doc_string label, Targets&&... tgts)
2264 {
2265     return parameter{std::forward<Filter>(filter)}
2266         .label(label)
2267         .target(std::forward<Targets>(tgts)...)
2268         .required(true).blocking(true).repeatable(true);
2269 }
2270 
2271 
2272 
2273 /*************************************************************************//**
2274  *
2275  * @brief makes optional, blocking value parameter;
2276  *        matches any non-empty string
2277  *
2278  *****************************************************************************/
2279 template<class... Targets>
2280 inline parameter
2281 opt_value(const doc_string& label, Targets&&... tgts)
2282 {
2283     return parameter{match::nonempty}
2284         .label(label)
2285         .target(std::forward<Targets>(tgts)...)
2286         .required(false).blocking(false).repeatable(false);
2287 }
2288 
2289 template<class Filter, class... Targets, class = typename std::enable_if<
2290     traits::is_callable<Filter,bool(const char*)>::value ||
2291     traits::is_callable<Filter,subrange(const char*)>::value>::type>
2292 inline parameter
2293 opt_value(Filter&& filter, doc_string label, Targets&&... tgts)
2294 {
2295     return parameter{std::forward<Filter>(filter)}
2296         .label(label)
2297         .target(std::forward<Targets>(tgts)...)
2298         .required(false).blocking(false).repeatable(false);
2299 }
2300 
2301 
2302 
2303 /*************************************************************************//**
2304  *
2305  * @brief makes optional, blocking, repeatable value parameter;
2306  *        matches any non-empty string
2307  *
2308  *****************************************************************************/
2309 template<class... Targets>
2310 inline parameter
2311 opt_values(const doc_string& label, Targets&&... tgts)
2312 {
2313     return parameter{match::nonempty}
2314         .label(label)
2315         .target(std::forward<Targets>(tgts)...)
2316         .required(false).blocking(false).repeatable(true);
2317 }
2318 
2319 template<class Filter, class... Targets, class = typename std::enable_if<
2320     traits::is_callable<Filter,bool(const char*)>::value ||
2321     traits::is_callable<Filter,subrange(const char*)>::value>::type>
2322 inline parameter
2323 opt_values(Filter&& filter, doc_string label, Targets&&... tgts)
2324 {
2325     return parameter{std::forward<Filter>(filter)}
2326         .label(label)
2327         .target(std::forward<Targets>(tgts)...)
2328         .required(false).blocking(false).repeatable(true);
2329 }
2330 
2331 
2332 
2333 /*************************************************************************//**
2334  *
2335  * @brief makes required, blocking value parameter;
2336  *        matches any string consisting of alphanumeric characters
2337  *
2338  *****************************************************************************/
2339 template<class... Targets>
2340 inline parameter
2341 word(const doc_string& label, Targets&&... tgts)
2342 {
2343     return parameter{match::alphanumeric}
2344         .label(label)
2345         .target(std::forward<Targets>(tgts)...)
2346         .required(true).blocking(true).repeatable(false);
2347 }
2348 
2349 
2350 
2351 /*************************************************************************//**
2352  *
2353  * @brief makes required, blocking, repeatable value parameter;
2354  *        matches any string consisting of alphanumeric characters
2355  *
2356  *****************************************************************************/
2357 template<class... Targets>
2358 inline parameter
2359 words(const doc_string& label, Targets&&... tgts)
2360 {
2361     return parameter{match::alphanumeric}
2362         .label(label)
2363         .target(std::forward<Targets>(tgts)...)
2364         .required(true).blocking(true).repeatable(true);
2365 }
2366 
2367 
2368 
2369 /*************************************************************************//**
2370  *
2371  * @brief makes optional, blocking value parameter;
2372  *        matches any string consisting of alphanumeric characters
2373  *
2374  *****************************************************************************/
2375 template<class... Targets>
2376 inline parameter
2377 opt_word(const doc_string& label, Targets&&... tgts)
2378 {
2379     return parameter{match::alphanumeric}
2380         .label(label)
2381         .target(std::forward<Targets>(tgts)...)
2382         .required(false).blocking(false).repeatable(false);
2383 }
2384 
2385 
2386 
2387 /*************************************************************************//**
2388  *
2389  * @brief makes optional, blocking, repeatable value parameter;
2390  *        matches any string consisting of alphanumeric characters
2391  *
2392  *****************************************************************************/
2393 template<class... Targets>
2394 inline parameter
2395 opt_words(const doc_string& label, Targets&&... tgts)
2396 {
2397     return parameter{match::alphanumeric}
2398         .label(label)
2399         .target(std::forward<Targets>(tgts)...)
2400         .required(false).blocking(false).repeatable(true);
2401 }
2402 
2403 
2404 
2405 /*************************************************************************//**
2406  *
2407  * @brief makes required, blocking value parameter;
2408  *        matches any string that represents a number
2409  *
2410  *****************************************************************************/
2411 template<class... Targets>
2412 inline parameter
2413 number(const doc_string& label, Targets&&... tgts)
2414 {
2415     return parameter{match::numbers{}}
2416         .label(label)
2417         .target(std::forward<Targets>(tgts)...)
2418         .required(true).blocking(true).repeatable(false);
2419 }
2420 
2421 
2422 
2423 /*************************************************************************//**
2424  *
2425  * @brief makes required, blocking, repeatable value parameter;
2426  *        matches any string that represents a number
2427  *
2428  *****************************************************************************/
2429 template<class... Targets>
2430 inline parameter
2431 numbers(const doc_string& label, Targets&&... tgts)
2432 {
2433     return parameter{match::numbers{}}
2434         .label(label)
2435         .target(std::forward<Targets>(tgts)...)
2436         .required(true).blocking(true).repeatable(true);
2437 }
2438 
2439 
2440 
2441 /*************************************************************************//**
2442  *
2443  * @brief makes optional, blocking value parameter;
2444  *        matches any string that represents a number
2445  *
2446  *****************************************************************************/
2447 template<class... Targets>
2448 inline parameter
2449 opt_number(const doc_string& label, Targets&&... tgts)
2450 {
2451     return parameter{match::numbers{}}
2452         .label(label)
2453         .target(std::forward<Targets>(tgts)...)
2454         .required(false).blocking(false).repeatable(false);
2455 }
2456 
2457 
2458 
2459 /*************************************************************************//**
2460  *
2461  * @brief makes optional, blocking, repeatable value parameter;
2462  *        matches any string that represents a number
2463  *
2464  *****************************************************************************/
2465 template<class... Targets>
2466 inline parameter
2467 opt_numbers(const doc_string& label, Targets&&... tgts)
2468 {
2469     return parameter{match::numbers{}}
2470         .label(label)
2471         .target(std::forward<Targets>(tgts)...)
2472         .required(false).blocking(false).repeatable(true);
2473 }
2474 
2475 
2476 
2477 /*************************************************************************//**
2478  *
2479  * @brief makes required, blocking value parameter;
2480  *        matches any string that represents an integer
2481  *
2482  *****************************************************************************/
2483 template<class... Targets>
2484 inline parameter
2485 integer(const doc_string& label, Targets&&... tgts)
2486 {
2487     return parameter{match::integers{}}
2488         .label(label)
2489         .target(std::forward<Targets>(tgts)...)
2490         .required(true).blocking(true).repeatable(false);
2491 }
2492 
2493 
2494 
2495 /*************************************************************************//**
2496  *
2497  * @brief makes required, blocking, repeatable value parameter;
2498  *        matches any string that represents an integer
2499  *
2500  *****************************************************************************/
2501 template<class... Targets>
2502 inline parameter
2503 integers(const doc_string& label, Targets&&... tgts)
2504 {
2505     return parameter{match::integers{}}
2506         .label(label)
2507         .target(std::forward<Targets>(tgts)...)
2508         .required(true).blocking(true).repeatable(true);
2509 }
2510 
2511 
2512 
2513 /*************************************************************************//**
2514  *
2515  * @brief makes optional, blocking value parameter;
2516  *        matches any string that represents an integer
2517  *
2518  *****************************************************************************/
2519 template<class... Targets>
2520 inline parameter
2521 opt_integer(const doc_string& label, Targets&&... tgts)
2522 {
2523     return parameter{match::integers{}}
2524         .label(label)
2525         .target(std::forward<Targets>(tgts)...)
2526         .required(false).blocking(false).repeatable(false);
2527 }
2528 
2529 
2530 
2531 /*************************************************************************//**
2532  *
2533  * @brief makes optional, blocking, repeatable value parameter;
2534  *        matches any string that represents an integer
2535  *
2536  *****************************************************************************/
2537 template<class... Targets>
2538 inline parameter
2539 opt_integers(const doc_string& label, Targets&&... tgts)
2540 {
2541     return parameter{match::integers{}}
2542         .label(label)
2543         .target(std::forward<Targets>(tgts)...)
2544         .required(false).blocking(false).repeatable(true);
2545 }
2546 
2547 
2548 
2549 /*************************************************************************//**
2550  *
2551  * @brief makes catch-all value parameter
2552  *
2553  *****************************************************************************/
2554 template<class... Targets>
2555 inline parameter
2556 any_other(Targets&&... tgts)
2557 {
2558     return parameter{match::any}
2559         .target(std::forward<Targets>(tgts)...)
2560         .required(false).blocking(false).repeatable(true);
2561 }
2562 
2563 
2564 
2565 /*************************************************************************//**
2566  *
2567  * @brief makes catch-all value parameter with custom filter
2568  *
2569  *****************************************************************************/
2570 template<class Filter, class... Targets, class = typename std::enable_if<
2571     traits::is_callable<Filter,bool(const char*)>::value ||
2572     traits::is_callable<Filter,subrange(const char*)>::value>::type>
2573 inline parameter
2574 any(Filter&& filter, Targets&&... tgts)
2575 {
2576     return parameter{std::forward<Filter>(filter)}
2577         .target(std::forward<Targets>(tgts)...)
2578         .required(false).blocking(false).repeatable(true);
2579 }
2580 
2581 
2582 
2583 
2584 /*************************************************************************//**
2585  *
2586  * @brief group of parameters and/or other groups;
2587  *        can be configured to act as a group of alternatives (exclusive match)
2588  *
2589  *****************************************************************************/
2590 class group :
2591     public detail::token<group>
2592 {
2593     //---------------------------------------------------------------
2594     /**
2595         * @brief tagged union type that either stores a parameter or a group
2596         *        and provides a common interface to them
2597         *        could be replaced by std::variant in the future
2598         *
2599         *        Note to future self: do NOT try again to do this with
2600         *        dynamic polymorphism; there are a couple of
2601         *        nasty problems associated with it and the implementation
2602         *        becomes bloated and needlessly complicated.
2603         */
2604     template<class Param, class Group>
2605     struct child_t {
2606         enum class type : char {param, group};
2607     public:
2608 
2609         explicit
2610         child_t(const Param&  v)          : m_{v},            type_{type::param} {}
2611         child_t(      Param&& v) noexcept : m_{std::move(v)}, type_{type::param} {}
2612 
2613         explicit
2614         child_t(const Group&  g)          : m_{g},            type_{type::group} {}
2615         child_t(      Group&& g) noexcept : m_{std::move(g)}, type_{type::group} {}
2616 
2617         child_t(const child_t& src): type_{src.type_} {
2618             switch(type_) {
2619                 default:
2620                 case type::param: new(&m_)data{src.m_.param}; break;
2621                 case type::group: new(&m_)data{src.m_.group}; break;
2622             }
2623         }
2624 
2625         child_t(child_t&& src) noexcept : type_{src.type_} {
2626             switch(type_) {
2627                 default:
2628                 case type::param: new(&m_)data{std::move(src.m_.param)}; break;
2629                 case type::group: new(&m_)data{std::move(src.m_.group)}; break;
2630             }
2631         }
2632 
2633         child_t& operator = (const child_t& src) {
2634             destroy_content();
2635             type_ = src.type_;
2636             switch(type_) {
2637                 default:
2638                 case type::param: new(&m_)data{src.m_.param}; break;
2639                 case type::group: new(&m_)data{src.m_.group}; break;
2640             }
2641             return *this;
2642         }
2643 
2644         child_t& operator = (child_t&& src) noexcept {
2645             destroy_content();
2646             type_ = src.type_;
2647             switch(type_) {
2648                 default:
2649                 case type::param: new(&m_)data{std::move(src.m_.param)}; break;
2650                 case type::group: new(&m_)data{std::move(src.m_.group)}; break;
2651             }
2652             return *this;
2653         }
2654 
2655         ~child_t() {
2656             destroy_content();
2657         }
2658 
2659         const doc_string&
2660         doc() const noexcept {
2661             switch(type_) {
2662                 default:
2663                 case type::param: return m_.param.doc();
2664                 case type::group: return m_.group.doc();
2665             }
2666         }
2667 
2668         bool blocking() const noexcept {
2669             switch(type_) {
2670                 case type::param: return m_.param.blocking();
2671                 case type::group: return m_.group.blocking();
2672                 default: return false;
2673             }
2674         }
2675         bool repeatable() const noexcept {
2676             switch(type_) {
2677                 case type::param: return m_.param.repeatable();
2678                 case type::group: return m_.group.repeatable();
2679                 default: return false;
2680             }
2681         }
2682         bool required() const noexcept {
2683             switch(type_) {
2684                 case type::param: return m_.param.required();
2685                 case type::group:
2686                     return (m_.group.exclusive() && m_.group.all_required() ) ||
2687                           (!m_.group.exclusive() && m_.group.any_required()  );
2688                 default: return false;
2689             }
2690         }
2691         bool exclusive() const noexcept {
2692             switch(type_) {
2693                 case type::group: return m_.group.exclusive();
2694                 case type::param:
2695                 default: return false;
2696             }
2697         }
2698         std::size_t param_count() const noexcept {
2699             switch(type_) {
2700                 case type::group: return m_.group.param_count();
2701                 case type::param:
2702                 default: return std::size_t(1);
2703             }
2704         }
2705         std::size_t depth() const noexcept {
2706             switch(type_) {
2707                 case type::group: return m_.group.depth();
2708                 case type::param:
2709                 default: return std::size_t(0);
2710             }
2711         }
2712 
2713         void execute_actions(const arg_string& arg) const {
2714             switch(type_) {
2715                 default:
2716                 case type::group: return;
2717                 case type::param: m_.param.execute_actions(arg); break;
2718             }
2719 
2720         }
2721 
2722         void notify_repeated(arg_index idx) const {
2723             switch(type_) {
2724                 default:
2725                 case type::group: return;
2726                 case type::param: m_.param.notify_repeated(idx); break;
2727             }
2728         }
2729         void notify_missing(arg_index idx) const {
2730             switch(type_) {
2731                 default:
2732                 case type::group: return;
2733                 case type::param: m_.param.notify_missing(idx); break;
2734             }
2735         }
2736         void notify_blocked(arg_index idx) const {
2737             switch(type_) {
2738                 default:
2739                 case type::group: return;
2740                 case type::param: m_.param.notify_blocked(idx); break;
2741             }
2742         }
2743         void notify_conflict(arg_index idx) const {
2744             switch(type_) {
2745                 default:
2746                 case type::group: return;
2747                 case type::param: m_.param.notify_conflict(idx); break;
2748             }
2749         }
2750 
2751         bool is_param() const noexcept { return type_ == type::param; }
2752         bool is_group() const noexcept { return type_ == type::group; }
2753 
2754         Param& as_param() noexcept { return m_.param; }
2755         Group& as_group() noexcept { return m_.group; }
2756 
2757         const Param& as_param() const noexcept { return m_.param; }
2758         const Group& as_group() const noexcept { return m_.group; }
2759 
2760     private:
2761         void destroy_content() {
2762             switch(type_) {
2763                 default:
2764                 case type::param: m_.param.~Param(); break;
2765                 case type::group: m_.group.~Group(); break;
2766             }
2767         }
2768 
2769         union data {
2770             data() {}
2771 
2772             data(const Param&  v)          : param{v} {}
2773             data(      Param&& v) noexcept : param{std::move(v)} {}
2774 
2775             data(const Group&  g)          : group{g} {}
2776             data(      Group&& g) noexcept : group{std::move(g)} {}
2777             ~data() {}
2778 
2779             Param param;
2780             Group group;
2781         };
2782 
2783         data m_;
2784         type type_;
2785     };
2786 
2787 
2788 public:
2789     //---------------------------------------------------------------
2790     using child      = child_t<parameter,group>;
2791     using value_type = child;
2792 
2793 private:
2794     using children_store = std::vector<child>;
2795 
2796 public:
2797     using const_iterator = children_store::const_iterator;
2798     using iterator       = children_store::iterator;
2799     using size_type      = children_store::size_type;
2800 
2801 
2802     //---------------------------------------------------------------
2803     /**
2804      * @brief recursively iterates over all nodes
2805      */
2806     class depth_first_traverser
2807     {
2808     public:
2809         //-----------------------------------------------------
2810         struct context {
2811             context() = default;
2812             context(const group& p):
2813                 parent{&p}, cur{p.begin()}, end{p.end()}
2814             {}
2815             const group* parent = nullptr;
2816             const_iterator cur;
2817             const_iterator end;
2818         };
2819         using context_list = std::vector<context>;
2820 
2821         //-----------------------------------------------------
2822         class memento {
2823             friend class depth_first_traverser;
2824             int level_;
2825             context context_;
2826         public:
2827             int level() const noexcept { return level_; }
2828             const child* param() const noexcept { return &(*context_.cur); }
2829         };
2830 
2831         depth_first_traverser() = default;
2832 
2833         explicit
2834         depth_first_traverser(const group& cur): stack_{} {
2835             if(!cur.empty()) stack_.emplace_back(cur);
2836         }
2837 
2838         explicit operator bool() const noexcept {
2839             return !stack_.empty();
2840         }
2841 
2842         int level() const noexcept {
2843             return int(stack_.size());
2844         }
2845 
2846         bool is_first_in_parent() const noexcept {
2847             if(stack_.empty()) return false;
2848             return (stack_.back().cur == stack_.back().parent->begin());
2849         }
2850 
2851         bool is_last_in_parent() const noexcept {
2852             if(stack_.empty()) return false;
2853             return (stack_.back().cur+1 == stack_.back().end);
2854         }
2855 
2856         bool is_last_in_path() const noexcept {
2857             if(stack_.empty()) return false;
2858             for(const auto& t : stack_) {
2859                 if(t.cur+1 != t.end) return false;
2860             }
2861             const auto& top = stack_.back();
2862             //if we have to descend into group on next ++ => not last in path
2863             if(top.cur->is_group()) return false;
2864             return true;
2865         }
2866 
2867         /** @brief inside a group of alternatives >= minlevel */
2868         bool is_alternative(int minlevel = 0) const noexcept {
2869             if(stack_.empty()) return false;
2870             if(minlevel > 0) minlevel -= 1;
2871             if(minlevel >= int(stack_.size())) return false;
2872             return std::any_of(stack_.begin() + minlevel, stack_.end(),
2873                 [](const context& c) { return c.parent->exclusive(); });
2874         }
2875 
2876         /** @brief repeatable or inside a repeatable group >= minlevel */
2877         bool is_repeatable(int minlevel = 0) const noexcept {
2878             if(stack_.empty()) return false;
2879             if(stack_.back().cur->repeatable()) return true;
2880             if(minlevel > 0) minlevel -= 1;
2881             if(minlevel >= int(stack_.size())) return false;
2882             return std::any_of(stack_.begin() + minlevel, stack_.end(),
2883                 [](const context& c) { return c.parent->repeatable(); });
2884         }
2885 
2886         /** @brief inside a particular group */
2887         bool is_inside(const group* g) const noexcept {
2888             if(!g) return false;
2889             return std::any_of(stack_.begin(), stack_.end(),
2890                 [g](const context& c) { return c.parent == g; });
2891         }
2892 
2893         /** @brief inside group with joinable flags */
2894         bool joinable() const noexcept {
2895             if(stack_.empty()) return false;
2896             return std::any_of(stack_.begin(), stack_.end(),
2897                 [](const context& c) { return c.parent->joinable(); });
2898         }
2899 
2900         const context_list&
2901         stack() const {
2902             return stack_;
2903         }
2904 
2905         /** @brief innermost repeat group */
2906         const group*
2907         innermost_repeat_group() const noexcept {
2908             auto i = std::find_if(stack_.rbegin(), stack_.rend(),
2909                 [](const context& c) { return c.parent->repeatable(); });
2910             return i != stack_.rend() ? i->parent : nullptr;
2911         }
2912 
2913         /** @brief innermost exclusive (alternatives) group */
2914         const group*
2915         innermost_exclusive_group() const noexcept {
2916             auto i = std::find_if(stack_.rbegin(), stack_.rend(),
2917                 [](const context& c) { return c.parent->exclusive(); });
2918             return i != stack_.rend() ? i->parent : nullptr;
2919         }
2920 
2921         /** @brief innermost blocking group */
2922         const group*
2923         innermost_blocking_group() const noexcept {
2924             auto i = std::find_if(stack_.rbegin(), stack_.rend(),
2925                 [](const context& c) { return c.parent->blocking(); });
2926             return i != stack_.rend() ? i->parent : nullptr;
2927         }
2928 
2929         /** @brief returns the outermost group that will be left on next ++*/
2930         const group*
2931         outermost_blocking_group_fully_explored() const noexcept {
2932             if(stack_.empty()) return nullptr;
2933 
2934             const group* g = nullptr;
2935             for(auto i = stack_.rbegin(); i != stack_.rend(); ++i) {
2936                 if(i->cur+1 == i->end) {
2937                     if(i->parent->blocking()) g = i->parent;
2938                 } else {
2939                     return g;
2940                 }
2941             }
2942             return g;
2943         }
2944 
2945         /** @brief outermost join group */
2946         const group*
2947         outermost_join_group() const noexcept {
2948             auto i = std::find_if(stack_.begin(), stack_.end(),
2949                 [](const context& c) { return c.parent->joinable(); });
2950             return i != stack_.end() ? i->parent : nullptr;
2951         }
2952 
2953         const group* root() const noexcept {
2954             return stack_.empty() ? nullptr : stack_.front().parent;
2955         }
2956 
2957         /** @brief common flag prefix of all flags in current group */
2958         arg_string common_flag_prefix() const noexcept {
2959             if(stack_.empty()) return "";
2960             auto g = outermost_join_group();
2961             return g ? g->common_flag_prefix() : arg_string("");
2962         }
2963 
2964         const child&
2965         operator * () const noexcept {
2966             return *stack_.back().cur;
2967         }
2968 
2969         const child*
2970         operator -> () const noexcept {
2971             return &(*stack_.back().cur);
2972         }
2973 
2974         const group&
2975         parent() const noexcept {
2976             return *(stack_.back().parent);
2977         }
2978 
2979 
2980         /** @brief go to next element of depth first search */
2981         depth_first_traverser&
2982         operator ++ () {
2983             if(stack_.empty()) return *this;
2984             //at group -> decend into group
2985             if(stack_.back().cur->is_group()) {
2986                 stack_.emplace_back(stack_.back().cur->as_group());
2987             }
2988             else {
2989                 next_sibling();
2990             }
2991             return *this;
2992         }
2993 
2994         /** @brief go to next sibling of current */
2995         depth_first_traverser&
2996         next_sibling() {
2997             if(stack_.empty()) return *this;
2998             ++stack_.back().cur;
2999             //at the end of current group?
3000             while(stack_.back().cur == stack_.back().end) {
3001                 //go to parent
3002                 stack_.pop_back();
3003                 if(stack_.empty()) return *this;
3004                 //go to next sibling in parent
3005                 ++stack_.back().cur;
3006             }
3007             return *this;
3008         }
3009 
3010         /** @brief go to next position after siblings of current */
3011         depth_first_traverser&
3012         next_after_siblings() {
3013             if(stack_.empty()) return *this;
3014             stack_.back().cur = stack_.back().end-1;
3015             next_sibling();
3016             return *this;
3017         }
3018 
3019         /**
3020          * @brief
3021          */
3022         depth_first_traverser&
3023         back_to_ancestor(const group* g) {
3024             if(!g) return *this;
3025             while(!stack_.empty()) {
3026                 const auto& top = stack_.back().cur;
3027                 if(top->is_group() && &(top->as_group()) == g) return *this;
3028                 stack_.pop_back();
3029             }
3030             return *this;
3031         }
3032 
3033         /** @brief don't visit next siblings, go back to parent on next ++
3034          *         note: renders siblings unreachable for *this
3035          **/
3036         depth_first_traverser&
3037         skip_siblings() {
3038             if(stack_.empty()) return *this;
3039             //future increments won't visit subsequent siblings:
3040             stack_.back().end = stack_.back().cur+1;
3041             return *this;
3042         }
3043 
3044         /** @brief skips all other alternatives in surrounding exclusive groups
3045          *         on next ++
3046          *         note: renders alternatives unreachable for *this
3047         */
3048         depth_first_traverser&
3049         skip_alternatives() {
3050             if(stack_.empty()) return *this;
3051 
3052             //exclude all other alternatives in surrounding groups
3053             //by making their current position the last one
3054             for(auto& c : stack_) {
3055                 if(c.parent && c.parent->exclusive() && c.cur < c.end)
3056                     c.end = c.cur+1;
3057             }
3058 
3059             return *this;
3060         }
3061 
3062         void invalidate() {
3063             stack_.clear();
3064         }
3065 
3066         inline friend bool operator == (const depth_first_traverser& a,
3067                                         const depth_first_traverser& b)
3068         {
3069             if(a.stack_.empty() || b.stack_.empty()) return false;
3070 
3071             //parents not the same -> different position
3072             if(a.stack_.back().parent != b.stack_.back().parent) return false;
3073 
3074             bool aEnd = a.stack_.back().cur == a.stack_.back().end;
3075             bool bEnd = b.stack_.back().cur == b.stack_.back().end;
3076             //either both at the end of the same parent => same position
3077             if(aEnd && bEnd) return true;
3078             //or only one at the end => not at the same position
3079             if(aEnd || bEnd) return false;
3080             return std::addressof(*a.stack_.back().cur) ==
3081                    std::addressof(*b.stack_.back().cur);
3082         }
3083         inline friend bool operator != (const depth_first_traverser& a,
3084                                         const depth_first_traverser& b)
3085         {
3086             return !(a == b);
3087         }
3088 
3089         memento
3090         undo_point() const {
3091             memento m;
3092             m.level_ = int(stack_.size());
3093             if(!stack_.empty()) m.context_ = stack_.back();
3094             return m;
3095         }
3096 
3097         void undo(const memento& m) {
3098             if(m.level_ < 1) return;
3099             if(m.level_ <= int(stack_.size())) {
3100                 stack_.erase(stack_.begin() + m.level_, stack_.end());
3101                 stack_.back() = m.context_;
3102             }
3103             else if(stack_.empty() && m.level_ == 1) {
3104                 stack_.push_back(m.context_);
3105             }
3106         }
3107 
3108     private:
3109         context_list stack_;
3110     };
3111 
3112 
3113     //---------------------------------------------------------------
3114     group() = default;
3115 
3116     template<class Param, class... Params>
3117     explicit
3118     group(doc_string docstr, Param param, Params... params):
3119         children_{}, exclusive_{false}, joinable_{false}, scoped_{true}
3120     {
3121         doc(std::move(docstr));
3122         push_back(std::move(param), std::move(params)...);
3123     }
3124 
3125     template<class... Params>
3126     explicit
3127     group(parameter param, Params... params):
3128         children_{}, exclusive_{false}, joinable_{false}, scoped_{true}
3129     {
3130         push_back(std::move(param), std::move(params)...);
3131     }
3132 
3133     template<class P2, class... Ps>
3134     explicit
3135     group(group p1, P2 p2, Ps... ps):
3136         children_{}, exclusive_{false}, joinable_{false}, scoped_{true}
3137     {
3138         push_back(std::move(p1), std::move(p2), std::move(ps)...);
3139     }
3140 
3141 
3142     //-----------------------------------------------------
3143     group(const group&) = default;
3144     group(group&&) = default;
3145 
3146 
3147     //---------------------------------------------------------------
3148     group& operator = (const group&) = default;
3149     group& operator = (group&&) = default;
3150 
3151 
3152     //---------------------------------------------------------------
3153     /** @brief determines if a command line argument can be matched by a
3154      *         combination of (partial) matches through any number of children
3155      */
3156     group& joinable(bool yes) {
3157         joinable_ = yes;
3158         return *this;
3159     }
3160 
3161     /** @brief returns if a command line argument can be matched by a
3162      *         combination of (partial) matches through any number of children
3163      */
3164     bool joinable() const noexcept {
3165         return joinable_;
3166     }
3167 
3168 
3169     //---------------------------------------------------------------
3170     /** @brief turns explicit scoping on or off
3171      *         operators , & | and other combinating functions will
3172      *         not merge groups that are marked as scoped
3173      */
3174     group& scoped(bool yes) {
3175         scoped_ = yes;
3176         return *this;
3177     }
3178 
3179     /** @brief returns true if operators , & | and other combinating functions
3180      *         will merge groups and false otherwise
3181      */
3182     bool scoped() const noexcept
3183     {
3184         return scoped_;
3185     }
3186 
3187 
3188     //---------------------------------------------------------------
3189     /** @brief determines if children are mutually exclusive alternatives */
3190     group& exclusive(bool yes) {
3191         exclusive_ = yes;
3192         return *this;
3193     }
3194     /** @brief returns if children are mutually exclusive alternatives */
3195     bool exclusive() const noexcept {
3196         return exclusive_;
3197     }
3198 
3199 
3200     //---------------------------------------------------------------
3201     /** @brief returns true, if any child is required to match */
3202     bool any_required() const
3203     {
3204         return std::any_of(children_.begin(), children_.end(),
3205             [](const child& n){ return n.required(); });
3206     }
3207     /** @brief returns true, if all children are required to match */
3208     bool all_required() const
3209     {
3210         return std::all_of(children_.begin(), children_.end(),
3211             [](const child& n){ return n.required(); });
3212     }
3213 
3214 
3215     //---------------------------------------------------------------
3216     /** @brief returns true if any child is optional (=non-required) */
3217     bool any_optional() const {
3218         return !all_required();
3219     }
3220     /** @brief returns true if all children are optional (=non-required) */
3221     bool all_optional() const {
3222         return !any_required();
3223     }
3224 
3225 
3226     //---------------------------------------------------------------
3227     /** @brief returns if the entire group is blocking / positional */
3228     bool blocking() const noexcept {
3229         return token<group>::blocking() || (exclusive() && all_blocking());
3230     }
3231     //-----------------------------------------------------
3232     /** @brief determines if the entire group is blocking / positional */
3233     group& blocking(bool yes) {
3234         return token<group>::blocking(yes);
3235     }
3236 
3237     //---------------------------------------------------------------
3238     /** @brief returns true if any child is blocking */
3239     bool any_blocking() const
3240     {
3241         return std::any_of(children_.begin(), children_.end(),
3242             [](const child& n){ return n.blocking(); });
3243     }
3244     //---------------------------------------------------------------
3245     /** @brief returns true if all children is blocking */
3246     bool all_blocking() const
3247     {
3248         return std::all_of(children_.begin(), children_.end(),
3249             [](const child& n){ return n.blocking(); });
3250     }
3251 
3252 
3253     //---------------------------------------------------------------
3254     /** @brief returns if any child is a value parameter (recursive) */
3255     bool any_flagless() const
3256     {
3257         return std::any_of(children_.begin(), children_.end(),
3258             [](const child& p){
3259                 return p.is_param() && p.as_param().flags().empty();
3260             });
3261     }
3262     /** @brief returns if all children are value parameters (recursive) */
3263     bool all_flagless() const
3264     {
3265         return std::all_of(children_.begin(), children_.end(),
3266             [](const child& p){
3267                 return p.is_param() && p.as_param().flags().empty();
3268             });
3269     }
3270 
3271 
3272     //---------------------------------------------------------------
3273     /** @brief adds child parameter at the end */
3274     group&
3275     push_back(const parameter& v) {
3276         children_.emplace_back(v);
3277         return *this;
3278     }
3279     //-----------------------------------------------------
3280     /** @brief adds child parameter at the end */
3281     group&
3282     push_back(parameter&& v) {
3283         children_.emplace_back(std::move(v));
3284         return *this;
3285     }
3286     //-----------------------------------------------------
3287     /** @brief adds child group at the end */
3288     group&
3289     push_back(const group& g) {
3290         children_.emplace_back(g);
3291         return *this;
3292     }
3293     //-----------------------------------------------------
3294     /** @brief adds child group at the end */
3295     group&
3296     push_back(group&& g) {
3297         children_.emplace_back(std::move(g));
3298         return *this;
3299     }
3300 
3301 
3302     //-----------------------------------------------------
3303     /** @brief adds children (groups and/or parameters) */
3304     template<class Param1, class Param2, class... Params>
3305     group&
3306     push_back(Param1&& param1, Param2&& param2, Params&&... params)
3307     {
3308         children_.reserve(children_.size() + 2 + sizeof...(params));
3309         push_back(std::forward<Param1>(param1));
3310         push_back(std::forward<Param2>(param2), std::forward<Params>(params)...);
3311         return *this;
3312     }
3313 
3314 
3315     //---------------------------------------------------------------
3316     /** @brief adds child parameter at the beginning */
3317     group&
3318     push_front(const parameter& v) {
3319         children_.emplace(children_.begin(), v);
3320         return *this;
3321     }
3322     //-----------------------------------------------------
3323     /** @brief adds child parameter at the beginning */
3324     group&
3325     push_front(parameter&& v) {
3326         children_.emplace(children_.begin(), std::move(v));
3327         return *this;
3328     }
3329     //-----------------------------------------------------
3330     /** @brief adds child group at the beginning */
3331     group&
3332     push_front(const group& g) {
3333         children_.emplace(children_.begin(), g);
3334         return *this;
3335     }
3336     //-----------------------------------------------------
3337     /** @brief adds child group at the beginning */
3338     group&
3339     push_front(group&& g) {
3340         children_.emplace(children_.begin(), std::move(g));
3341         return *this;
3342     }
3343 
3344 
3345     //---------------------------------------------------------------
3346     /** @brief adds all children of other group at the end */
3347     group&
3348     merge(group&& g)
3349     {
3350         children_.insert(children_.end(),
3351                       std::make_move_iterator(g.begin()),
3352                       std::make_move_iterator(g.end()));
3353         return *this;
3354     }
3355     //-----------------------------------------------------
3356     /** @brief adds all children of several other groups at the end */
3357     template<class... Groups>
3358     group&
3359     merge(group&& g1, group&& g2, Groups&&... gs)
3360     {
3361         merge(std::move(g1));
3362         merge(std::move(g2), std::forward<Groups>(gs)...);
3363         return *this;
3364     }
3365 
3366 
3367     //---------------------------------------------------------------
3368     /** @brief indexed, nutable access to child */
3369     child& operator [] (size_type index) noexcept {
3370         return children_[index];
3371     }
3372     /** @brief indexed, non-nutable access to child */
3373     const child& operator [] (size_type index) const noexcept {
3374         return children_[index];
3375     }
3376 
3377     //---------------------------------------------------------------
3378     /** @brief mutable access to first child */
3379           child& front()       noexcept { return children_.front(); }
3380     /** @brief non-mutable access to first child */
3381     const child& front() const noexcept { return children_.front(); }
3382     //-----------------------------------------------------
3383     /** @brief mutable access to last child */
3384           child& back()       noexcept { return children_.back(); }
3385     /** @brief non-mutable access to last child */
3386     const child& back() const noexcept { return children_.back(); }
3387 
3388 
3389     //---------------------------------------------------------------
3390     /** @brief returns true, if group has no children, false otherwise */
3391     bool empty() const noexcept     { return children_.empty(); }
3392 
3393     /** @brief returns number of children */
3394     size_type size() const noexcept { return children_.size(); }
3395 
3396     /** @brief returns number of nested levels; 1 for a flat group */
3397     size_type depth() const {
3398         size_type n = 0;
3399         for(const auto& c : children_) {
3400             auto l = 1 + c.depth();
3401             if(l > n) n = l;
3402         }
3403         return n;
3404     }
3405 
3406 
3407     //---------------------------------------------------------------
3408     /** @brief returns mutating iterator to position of first element */
3409           iterator  begin()       noexcept { return children_.begin(); }
3410     /** @brief returns non-mutating iterator to position of first element */
3411     const_iterator  begin() const noexcept { return children_.begin(); }
3412     /** @brief returns non-mutating iterator to position of first element */
3413     const_iterator cbegin() const noexcept { return children_.begin(); }
3414 
3415     /** @brief returns mutating iterator to position one past the last element */
3416           iterator  end()         noexcept { return children_.end(); }
3417     /** @brief returns non-mutating iterator to position one past the last element */
3418     const_iterator  end()   const noexcept { return children_.end(); }
3419     /** @brief returns non-mutating iterator to position one past the last element */
3420     const_iterator cend()   const noexcept { return children_.end(); }
3421 
3422 
3423     //---------------------------------------------------------------
3424     /** @brief returns augmented iterator for depth first searches
3425      *  @details traverser knows end of iteration and can skip over children
3426      */
3427     depth_first_traverser
3428     begin_dfs() const noexcept {
3429         return depth_first_traverser{*this};
3430     }
3431 
3432 
3433     //---------------------------------------------------------------
3434     /** @brief returns recursive parameter count */
3435     size_type param_count() const {
3436         size_type c = 0;
3437         for(const auto& n : children_) {
3438             c += n.param_count();
3439         }
3440         return c;
3441     }
3442 
3443 
3444     //---------------------------------------------------------------
3445     /** @brief returns range of all flags (recursive) */
3446     arg_list all_flags() const
3447     {
3448         std::vector<arg_string> all;
3449         gather_flags(children_, all);
3450         return all;
3451     }
3452 
3453     /** @brief returns true, if no flag occurs as true
3454      *         prefix of any other flag (identical flags will be ignored) */
3455     bool flags_are_prefix_free() const
3456     {
3457         const auto fs = all_flags();
3458 
3459         using std::begin; using std::end;
3460         for(auto i = begin(fs), e = end(fs); i != e; ++i) {
3461             if(!i->empty()) {
3462                 for(auto j = i+1; j != e; ++j) {
3463                     if(!j->empty() && *i != *j) {
3464                         if(i->find(*j) == 0) return false;
3465                         if(j->find(*i) == 0) return false;
3466                     }
3467                 }
3468             }
3469         }
3470 
3471         return true;
3472     }
3473 
3474 
3475     //---------------------------------------------------------------
3476     /** @brief returns longest common prefix of all flags */
3477     arg_string common_flag_prefix() const
3478     {
3479         arg_list prefixes;
3480         gather_prefixes(children_, prefixes);
3481         return str::longest_common_prefix(prefixes);
3482     }
3483 
3484 
3485 private:
3486     //---------------------------------------------------------------
3487     static void
3488     gather_flags(const children_store& nodes, arg_list& all)
3489     {
3490         for(const auto& p : nodes) {
3491             if(p.is_group()) {
3492                 gather_flags(p.as_group().children_, all);
3493             }
3494             else {
3495                 const auto& pf = p.as_param().flags();
3496                 using std::begin;
3497                 using std::end;
3498                 if(!pf.empty()) all.insert(end(all), begin(pf), end(pf));
3499             }
3500         }
3501     }
3502     //---------------------------------------------------------------
3503     static void
3504     gather_prefixes(const children_store& nodes, arg_list& all)
3505     {
3506         for(const auto& p : nodes) {
3507             if(p.is_group()) {
3508                 gather_prefixes(p.as_group().children_, all);
3509             }
3510             else if(!p.as_param().flags().empty()) {
3511                 auto pfx = str::longest_common_prefix(p.as_param().flags());
3512                 if(!pfx.empty()) all.push_back(std::move(pfx));
3513             }
3514         }
3515     }
3516 
3517     //---------------------------------------------------------------
3518     children_store children_;
3519     bool exclusive_ = false;
3520     bool joinable_ = false;
3521     bool scoped_ = false;
3522 };
3523 
3524 
3525 
3526 /*************************************************************************//**
3527  *
3528  * @brief group or parameter
3529  *
3530  *****************************************************************************/
3531 using pattern = group::child;
3532 
3533 
3534 
3535 /*************************************************************************//**
3536  *
3537  * @brief apply an action to all parameters in a group
3538  *
3539  *****************************************************************************/
3540 template<class Action>
3541 void for_all_params(group& g, Action&& action)
3542 {
3543     for(auto& p : g) {
3544         if(p.is_group()) {
3545             for_all_params(p.as_group(), action);
3546         }
3547         else {
3548             action(p.as_param());
3549         }
3550     }
3551 }
3552 
3553 template<class Action>
3554 void for_all_params(const group& g, Action&& action)
3555 {
3556     for(auto& p : g) {
3557         if(p.is_group()) {
3558             for_all_params(p.as_group(), action);
3559         }
3560         else {
3561             action(p.as_param());
3562         }
3563     }
3564 }
3565 
3566 
3567 
3568 /*************************************************************************//**
3569  *
3570  * @brief makes a group of parameters and/or groups
3571  *
3572  *****************************************************************************/
3573 inline group
3574 operator , (parameter a, parameter b)
3575 {
3576     return group{std::move(a), std::move(b)}.scoped(false);
3577 }
3578 
3579 //---------------------------------------------------------
3580 inline group
3581 operator , (parameter a, group b)
3582 {
3583     return !b.scoped() && !b.blocking() && !b.exclusive() && !b.repeatable()
3584         && !b.joinable() && (b.doc().empty() || b.doc() == a.doc())
3585        ? b.push_front(std::move(a))
3586        : group{std::move(a), std::move(b)}.scoped(false);
3587 }
3588 
3589 //---------------------------------------------------------
3590 inline group
3591 operator , (group a, parameter b)
3592 {
3593     return !a.scoped() && !a.blocking() && !a.exclusive() && !a.repeatable()
3594         && !a.joinable() && (a.doc().empty() || a.doc() == b.doc())
3595        ? a.push_back(std::move(b))
3596        : group{std::move(a), std::move(b)}.scoped(false);
3597 }
3598 
3599 //---------------------------------------------------------
3600 inline group
3601 operator , (group a, group b)
3602 {
3603     return !a.scoped() && !a.blocking() && !a.exclusive() && !a.repeatable()
3604         && !a.joinable() && (a.doc().empty() || a.doc() == b.doc())
3605        ? a.push_back(std::move(b))
3606        : group{std::move(a), std::move(b)}.scoped(false);
3607 }
3608 
3609 
3610 
3611 /*************************************************************************//**
3612  *
3613  * @brief makes a group of alternative parameters or groups
3614  *
3615  *****************************************************************************/
3616 template<class Param, class... Params>
3617 inline group
3618 one_of(Param param, Params... params)
3619 {
3620     return group{std::move(param), std::move(params)...}.exclusive(true);
3621 }
3622 
3623 
3624 /*************************************************************************//**
3625  *
3626  * @brief makes a group of alternative parameters or groups
3627  *
3628  *****************************************************************************/
3629 inline group
3630 operator | (parameter a, parameter b)
3631 {
3632     return group{std::move(a), std::move(b)}.scoped(false).exclusive(true);
3633 }
3634 
3635 //-------------------------------------------------------------------
3636 inline group
3637 operator | (parameter a, group b)
3638 {
3639     return !b.scoped() && !b.blocking() && b.exclusive() && !b.repeatable()
3640         && !b.joinable()
3641         && (b.doc().empty() || b.doc() == a.doc())
3642         ? b.push_front(std::move(a))
3643         : group{std::move(a), std::move(b)}.scoped(false).exclusive(true);
3644 }
3645 
3646 //-------------------------------------------------------------------
3647 inline group
3648 operator | (group a, parameter b)
3649 {
3650     return !a.scoped() && a.exclusive() && !a.repeatable() && !a.joinable()
3651         && a.blocking() == b.blocking()
3652         && (a.doc().empty() || a.doc() == b.doc())
3653         ? a.push_back(std::move(b))
3654         : group{std::move(a), std::move(b)}.scoped(false).exclusive(true);
3655 }
3656 
3657 inline group
3658 operator | (group a, group b)
3659 {
3660     return !a.scoped() && a.exclusive() &&!a.repeatable() && !a.joinable()
3661         && a.blocking() == b.blocking()
3662         && (a.doc().empty() || a.doc() == b.doc())
3663         ? a.push_back(std::move(b))
3664         : group{std::move(a), std::move(b)}.scoped(false).exclusive(true);
3665 }
3666 
3667 
3668 
3669 /*************************************************************************//**
3670  *
3671  * @brief helpers (NOT FOR DIRECT USE IN CLIENT CODE!)
3672  *        no interface guarantees; might be changed or removed in the future
3673  *
3674  *****************************************************************************/
3675 namespace detail {
3676 
3677 inline void set_blocking(bool) {}
3678 
3679 template<class P, class... Ps>
3680 void set_blocking(bool yes, P& p, Ps&... ps) {
3681     p.blocking(yes);
3682     set_blocking(yes, ps...);
3683 }
3684 
3685 } // namespace detail
3686 
3687 
3688 /*************************************************************************//**
3689  *
3690  * @brief makes a parameter/group sequence by making all input objects blocking
3691  *
3692  *****************************************************************************/
3693 template<class Param, class... Params>
3694 inline group
3695 in_sequence(Param param, Params... params)
3696 {
3697     detail::set_blocking(true, param, params...);
3698     return group{std::move(param), std::move(params)...}.scoped(true);
3699 }
3700 
3701 
3702 /*************************************************************************//**
3703  *
3704  * @brief makes a parameter/group sequence by making all input objects blocking
3705  *
3706  *****************************************************************************/
3707 inline group
3708 operator & (parameter a, parameter b)
3709 {
3710     a.blocking(true);
3711     b.blocking(true);
3712     return group{std::move(a), std::move(b)}.scoped(true);
3713 }
3714 
3715 //---------------------------------------------------------
3716 inline group
3717 operator & (parameter a, group b)
3718 {
3719     a.blocking(true);
3720     return group{std::move(a), std::move(b)}.scoped(true);
3721 }
3722 
3723 //---------------------------------------------------------
3724 inline group
3725 operator & (group a, parameter b)
3726 {
3727     b.blocking(true);
3728     if(a.all_blocking() && !a.exclusive() && !a.repeatable() && !a.joinable()
3729         && (a.doc().empty() || a.doc() == b.doc()))
3730     {
3731         return a.push_back(std::move(b));
3732     }
3733     else {
3734         if(!a.all_blocking()) a.blocking(true);
3735         return group{std::move(a), std::move(b)}.scoped(true);
3736     }
3737 }
3738 
3739 inline group
3740 operator & (group a, group b)
3741 {
3742     if(!b.all_blocking()) b.blocking(true);
3743     if(a.all_blocking() && !a.exclusive() && !a.repeatable()
3744         && !a.joinable() && (a.doc().empty() || a.doc() == b.doc()))
3745     {
3746         return a.push_back(std::move(b));
3747     }
3748     else {
3749         if(!a.all_blocking()) a.blocking(true);
3750         return group{std::move(a), std::move(b)}.scoped(true);
3751     }
3752 }
3753 
3754 
3755 
3756 /*************************************************************************//**
3757  *
3758  * @brief makes a group of parameters and/or groups
3759  *        where all single char flag params ("-a", "b", ...) are joinable
3760  *
3761  *****************************************************************************/
3762 inline group
3763 joinable(group g) {
3764     return g.joinable(true);
3765 }
3766 
3767 //-------------------------------------------------------------------
3768 template<class... Params>
3769 inline group
3770 joinable(parameter param, Params... params)
3771 {
3772     return group{std::move(param), std::move(params)...}.joinable(true);
3773 }
3774 
3775 template<class P2, class... Ps>
3776 inline group
3777 joinable(group p1, P2 p2, Ps... ps)
3778 {
3779     return group{std::move(p1), std::move(p2), std::move(ps)...}.joinable(true);
3780 }
3781 
3782 template<class Param, class... Params>
3783 inline group
3784 joinable(doc_string docstr, Param param, Params... params)
3785 {
3786     return group{std::move(param), std::move(params)...}
3787                 .joinable(true).doc(std::move(docstr));
3788 }
3789 
3790 
3791 
3792 /*************************************************************************//**
3793  *
3794  * @brief makes a repeatable copy of a parameter
3795  *
3796  *****************************************************************************/
3797 inline parameter
3798 repeatable(parameter p) {
3799     return p.repeatable(true);
3800 }
3801 
3802 /*************************************************************************//**
3803  *
3804  * @brief makes a repeatable copy of a group
3805  *
3806  *****************************************************************************/
3807 inline group
3808 repeatable(group g) {
3809     return g.repeatable(true);
3810 }
3811 
3812 
3813 
3814 /*************************************************************************//**
3815  *
3816  * @brief makes a group of parameters and/or groups
3817  *        that is repeatable as a whole
3818  *        Note that a repeatable group consisting entirely of non-blocking
3819  *        children is equivalent to a non-repeatable group of
3820  *        repeatable children.
3821  *
3822  *****************************************************************************/
3823 template<class P2, class... Ps>
3824 inline group
3825 repeatable(parameter p1, P2 p2, Ps... ps)
3826 {
3827     return group{std::move(p1), std::move(p2),
3828                  std::move(ps)...}.repeatable(true);
3829 }
3830 
3831 template<class P2, class... Ps>
3832 inline group
3833 repeatable(group p1, P2 p2, Ps... ps)
3834 {
3835     return group{std::move(p1), std::move(p2),
3836                  std::move(ps)...}.repeatable(true);
3837 }
3838 
3839 
3840 
3841 /*************************************************************************//**
3842  *
3843  * @brief makes a parameter greedy (match with top priority)
3844  *
3845  *****************************************************************************/
3846 inline parameter
3847 greedy(parameter p) {
3848     return p.greedy(true);
3849 }
3850 
3851 inline parameter
3852 operator ! (parameter p) {
3853     return greedy(p);
3854 }
3855 
3856 
3857 
3858 /*************************************************************************//**
3859  *
3860  * @brief recursively prepends a prefix to all flags
3861  *
3862  *****************************************************************************/
3863 inline parameter&&
3864 with_prefix(const arg_string& prefix, parameter&& p) {
3865     return std::move(with_prefix(prefix, p));
3866 }
3867 
3868 
3869 //-------------------------------------------------------------------
3870 inline group&
3871 with_prefix(const arg_string& prefix, group& g)
3872 {
3873     for(auto& p : g) {
3874         if(p.is_group()) {
3875             with_prefix(prefix, p.as_group());
3876         } else {
3877             with_prefix(prefix, p.as_param());
3878         }
3879     }
3880     return g;
3881 }
3882 
3883 
3884 inline group&&
3885 with_prefix(const arg_string& prefix, group&& params)
3886 {
3887     return std::move(with_prefix(prefix, params));
3888 }
3889 
3890 
3891 template<class Param, class... Params>
3892 inline group
3893 with_prefix(arg_string prefix, Param&& param, Params&&... params)
3894 {
3895     return with_prefix(prefix, group{std::forward<Param>(param),
3896                                      std::forward<Params>(params)...});
3897 }
3898 
3899 
3900 
3901 /*************************************************************************//**
3902  *
3903  * @brief recursively prepends a prefix to all flags
3904  *
3905  * @param shortpfx : used for single-letter flags
3906  * @param longpfx  : used for flags with length > 1
3907  *
3908  *****************************************************************************/
3909 inline parameter&&
3910 with_prefixes_short_long(const arg_string& shortpfx, const arg_string& longpfx,
3911                          parameter&& p)
3912 {
3913     return std::move(with_prefixes_short_long(shortpfx, longpfx, p));
3914 }
3915 
3916 
3917 //-------------------------------------------------------------------
3918 inline group&
3919 with_prefixes_short_long(const arg_string& shortFlagPrefix,
3920                          const arg_string& longFlagPrefix,
3921                          group& g)
3922 {
3923     for(auto& p : g) {
3924         if(p.is_group()) {
3925             with_prefixes_short_long(shortFlagPrefix, longFlagPrefix, p.as_group());
3926         } else {
3927             with_prefixes_short_long(shortFlagPrefix, longFlagPrefix, p.as_param());
3928         }
3929     }
3930     return g;
3931 }
3932 
3933 
3934 inline group&&
3935 with_prefixes_short_long(const arg_string& shortFlagPrefix,
3936                          const arg_string& longFlagPrefix,
3937                          group&& params)
3938 {
3939     return std::move(with_prefixes_short_long(shortFlagPrefix, longFlagPrefix,
3940                                               params));
3941 }
3942 
3943 
3944 template<class Param, class... Params>
3945 inline group
3946 with_prefixes_short_long(const arg_string& shortFlagPrefix,
3947                          const arg_string& longFlagPrefix,
3948                          Param&& param, Params&&... params)
3949 {
3950     return with_prefixes_short_long(shortFlagPrefix, longFlagPrefix,
3951                                     group{std::forward<Param>(param),
3952                                           std::forward<Params>(params)...});
3953 }
3954 
3955 
3956 
3957 /*************************************************************************//**
3958  *
3959  * @brief recursively prepends a suffix to all flags
3960  *
3961  *****************************************************************************/
3962 inline parameter&&
3963 with_suffix(const arg_string& suffix, parameter&& p) {
3964     return std::move(with_suffix(suffix, p));
3965 }
3966 
3967 
3968 //-------------------------------------------------------------------
3969 inline group&
3970 with_suffix(const arg_string& suffix, group& g)
3971 {
3972     for(auto& p : g) {
3973         if(p.is_group()) {
3974             with_suffix(suffix, p.as_group());
3975         } else {
3976             with_suffix(suffix, p.as_param());
3977         }
3978     }
3979     return g;
3980 }
3981 
3982 
3983 inline group&&
3984 with_suffix(const arg_string& suffix, group&& params)
3985 {
3986     return std::move(with_suffix(suffix, params));
3987 }
3988 
3989 
3990 template<class Param, class... Params>
3991 inline group
3992 with_suffix(arg_string suffix, Param&& param, Params&&... params)
3993 {
3994     return with_suffix(suffix, group{std::forward<Param>(param),
3995                                      std::forward<Params>(params)...});
3996 }
3997 
3998 
3999 
4000 /*************************************************************************//**
4001  *
4002  * @brief recursively prepends a suffix to all flags
4003  *
4004  * @param shortsfx : used for single-letter flags
4005  * @param longsfx  : used for flags with length > 1
4006  *
4007  *****************************************************************************/
4008 inline parameter&&
4009 with_suffixes_short_long(const arg_string& shortsfx, const arg_string& longsfx,
4010                          parameter&& p)
4011 {
4012     return std::move(with_suffixes_short_long(shortsfx, longsfx, p));
4013 }
4014 
4015 
4016 //-------------------------------------------------------------------
4017 inline group&
4018 with_suffixes_short_long(const arg_string& shortFlagSuffix,
4019                          const arg_string& longFlagSuffix,
4020                          group& g)
4021 {
4022     for(auto& p : g) {
4023         if(p.is_group()) {
4024             with_suffixes_short_long(shortFlagSuffix, longFlagSuffix, p.as_group());
4025         } else {
4026             with_suffixes_short_long(shortFlagSuffix, longFlagSuffix, p.as_param());
4027         }
4028     }
4029     return g;
4030 }
4031 
4032 
4033 inline group&&
4034 with_suffixes_short_long(const arg_string& shortFlagSuffix,
4035                          const arg_string& longFlagSuffix,
4036                          group&& params)
4037 {
4038     return std::move(with_suffixes_short_long(shortFlagSuffix, longFlagSuffix,
4039                                               params));
4040 }
4041 
4042 
4043 template<class Param, class... Params>
4044 inline group
4045 with_suffixes_short_long(const arg_string& shortFlagSuffix,
4046                          const arg_string& longFlagSuffix,
4047                          Param&& param, Params&&... params)
4048 {
4049     return with_suffixes_short_long(shortFlagSuffix, longFlagSuffix,
4050                                     group{std::forward<Param>(param),
4051                                           std::forward<Params>(params)...});
4052 }
4053 
4054 
4055 
4056 
4057 
4058 
4059 
4060 
4061 /*************************************************************************//**
4062  *
4063  * @brief parsing implementation details
4064  *
4065  *****************************************************************************/
4066 
4067 namespace detail {
4068 
4069 
4070 /*************************************************************************//**
4071  *
4072  * @brief DFS traverser that keeps track of 'scopes'
4073  *        scope = all parameters that are either bounded by
4074  *        two blocking parameters on the same depth level
4075  *        or the beginning/end of the outermost group
4076  *
4077  *****************************************************************************/
4078 class scoped_dfs_traverser
4079 {
4080 public:
4081     using dfs_traverser = group::depth_first_traverser;
4082 
4083     scoped_dfs_traverser() = default;
4084 
4085     explicit
4086     scoped_dfs_traverser(const group& g):
4087         pos_{g}, lastMatch_{}, posAfterLastMatch_{}, scopes_{},
4088         ignoreBlocks_{false},
4089         repeatGroupStarted_{false}, repeatGroupContinues_{false}
4090     {}
4091 
4092     const dfs_traverser& base() const noexcept { return pos_; }
4093     const dfs_traverser& last_match() const noexcept { return lastMatch_; }
4094 
4095     const group& parent() const noexcept { return pos_.parent(); }
4096 
4097     const group* innermost_repeat_group() const noexcept {
4098         return pos_.innermost_repeat_group();
4099     }
4100     const group* outermost_join_group() const noexcept {
4101         return pos_.outermost_join_group();
4102     }
4103     const group* innermost_blocking_group() const noexcept {
4104         return pos_.innermost_blocking_group();
4105     }
4106     const group* innermost_exclusive_group() const noexcept {
4107         return pos_.innermost_exclusive_group();
4108     }
4109 
4110     const pattern* operator ->() const noexcept { return pos_.operator->(); }
4111     const pattern& operator *() const noexcept { return *pos_; }
4112 
4113     const pattern* ptr() const noexcept { return pos_.operator->(); }
4114 
4115     explicit operator bool() const noexcept { return bool(pos_); }
4116 
4117     bool joinable() const noexcept { return pos_.joinable(); }
4118     arg_string common_flag_prefix() const { return pos_.common_flag_prefix(); }
4119 
4120     void ignore_blocking(bool yes) { ignoreBlocks_ = yes; }
4121 
4122     void invalidate() {
4123         pos_.invalidate();
4124     }
4125 
4126     bool matched() const noexcept {
4127         return (pos_ == lastMatch_);
4128     }
4129 
4130     bool start_of_repeat_group() const noexcept { return repeatGroupStarted_; }
4131 
4132     //-----------------------------------------------------
4133     scoped_dfs_traverser&
4134     next_sibling() { pos_.next_sibling(); return *this; }
4135 
4136     scoped_dfs_traverser&
4137     next_after_siblings() { pos_.next_after_siblings(); return *this; }
4138 
4139 
4140     //-----------------------------------------------------
4141     scoped_dfs_traverser&
4142     operator ++ ()
4143     {
4144         if(!pos_) return *this;
4145 
4146         if(pos_.is_last_in_path()) {
4147             return_to_outermost_scope();
4148             return *this;
4149         }
4150 
4151         //current pattern can block if it didn't match already
4152         if(ignoreBlocks_ || matched()) {
4153             ++pos_;
4154         }
4155         else if(!pos_->is_group()) {
4156             //current group can block if we didn't have any match in it
4157             const group* g = pos_.outermost_blocking_group_fully_explored();
4158             //no match in 'g' before -> skip to after its siblings
4159             if(g && !lastMatch_.is_inside(g)) {
4160                 pos_.back_to_ancestor(g).next_after_siblings();
4161                 if(!pos_) return_to_outermost_scope();
4162             }
4163             else if(pos_->blocking()) {
4164                 if(pos_.parent().exclusive()) {
4165                     pos_.next_sibling();
4166                 } else {
4167                     //no match => skip siblings of blocking param
4168                     pos_.next_after_siblings();
4169                 }
4170                 if(!pos_) return_to_outermost_scope();
4171             } else {
4172                 ++pos_;
4173             }
4174         } else {
4175             ++pos_;
4176         }
4177         check_if_left_scope();
4178         return *this;
4179     }
4180 
4181     //-----------------------------------------------------
4182     void next_after_match(scoped_dfs_traverser match)
4183     {
4184         if(!match || ignoreBlocks_) return;
4185 
4186         check_repeat_group_start(match);
4187 
4188         lastMatch_ = match.base();
4189 
4190         // if there is a blocking ancestor -> go back to it
4191         if(!match->blocking()) {
4192             match.pos_.back_to_ancestor(match.innermost_blocking_group());
4193         }
4194 
4195         //if match is not in current position & current position is blocking
4196         //=> current position has to be advanced by one so that it is
4197         //no longer reachable within current scope
4198         //(can happen for repeatable, blocking parameters)
4199         if(match.base() != pos_ && pos_->blocking()) pos_.next_sibling();
4200 
4201         if(match->blocking()) {
4202             if(match.pos_.is_alternative()) {
4203                 //discard other alternatives
4204                 match.pos_.skip_alternatives();
4205             }
4206 
4207             if(is_last_in_current_scope(match.pos_)) {
4208                 //if current param is not repeatable -> back to previous scope
4209                 if(!match->repeatable() && !match->is_group()) {
4210                     pos_ = std::move(match.pos_);
4211                     if(!scopes_.empty()) pos_.undo(scopes_.top());
4212                 }
4213                 else { //stay at match position
4214                     pos_ = std::move(match.pos_);
4215                 }
4216             }
4217             else { //not last in current group
4218                 //if current param is not repeatable, go directly to next
4219                 if(!match->repeatable() && !match->is_group()) {
4220                     ++match.pos_;
4221                 }
4222 
4223                 if(match.pos_.level() > pos_.level()) {
4224                     scopes_.push(pos_.undo_point());
4225                     pos_ = std::move(match.pos_);
4226                 }
4227                 else if(match.pos_.level() < pos_.level()) {
4228                     return_to_level(match.pos_.level());
4229                 }
4230                 else {
4231                     pos_ = std::move(match.pos_);
4232                 }
4233             }
4234             posAfterLastMatch_ = pos_;
4235         }
4236         else {
4237             if(match.pos_.level() < pos_.level()) {
4238                 return_to_level(match.pos_.level());
4239             }
4240             posAfterLastMatch_ = pos_;
4241         }
4242         repeatGroupContinues_ = repeat_group_continues();
4243     }
4244 
4245 private:
4246     //-----------------------------------------------------
4247     bool is_last_in_current_scope(const dfs_traverser& pos) const
4248     {
4249         if(scopes_.empty()) return pos.is_last_in_path();
4250         //check if we would leave the current scope on ++
4251         auto p = pos;
4252         ++p;
4253         return p.level() < scopes_.top().level();
4254     }
4255 
4256     //-----------------------------------------------------
4257     void check_repeat_group_start(const scoped_dfs_traverser& newMatch)
4258     {
4259         const auto newrg = newMatch.innermost_repeat_group();
4260         if(!newrg) {
4261             repeatGroupStarted_ = false;
4262         }
4263         else if(lastMatch_.innermost_repeat_group() != newrg) {
4264             repeatGroupStarted_ = true;
4265         }
4266         else if(!repeatGroupContinues_ || !newMatch.repeatGroupContinues_) {
4267             repeatGroupStarted_ = true;
4268         }
4269         else {
4270             //special case: repeat group is outermost group
4271             //=> we can never really 'leave' and 'reenter' it
4272             //but if the current scope is the first element, then we are
4273             //conceptually at a position 'before' the group
4274             repeatGroupStarted_ = scopes_.empty() || (
4275                     newrg == pos_.root() &&
4276                     scopes_.top().param() == &(*pos_.root()->begin()) );
4277         }
4278         repeatGroupContinues_ = repeatGroupStarted_;
4279     }
4280 
4281     //-----------------------------------------------------
4282     bool repeat_group_continues() const
4283     {
4284         if(!repeatGroupContinues_) return false;
4285         const auto curRepGroup = pos_.innermost_repeat_group();
4286         if(!curRepGroup) return false;
4287         if(curRepGroup != lastMatch_.innermost_repeat_group()) return false;
4288         if(!posAfterLastMatch_) return false;
4289         return true;
4290     }
4291 
4292     //-----------------------------------------------------
4293     void check_if_left_scope()
4294     {
4295         if(posAfterLastMatch_) {
4296             if(pos_.level() < posAfterLastMatch_.level()) {
4297                 while(!scopes_.empty() && scopes_.top().level() >= pos_.level()) {
4298                     pos_.undo(scopes_.top());
4299                     scopes_.pop();
4300                 }
4301                 posAfterLastMatch_.invalidate();
4302             }
4303         }
4304         while(!scopes_.empty() && scopes_.top().level() > pos_.level()) {
4305             pos_.undo(scopes_.top());
4306             scopes_.pop();
4307         }
4308         repeatGroupContinues_ = repeat_group_continues();
4309     }
4310 
4311     //-----------------------------------------------------
4312     void return_to_outermost_scope()
4313     {
4314         posAfterLastMatch_.invalidate();
4315 
4316         if(scopes_.empty()) {
4317             pos_.invalidate();
4318             repeatGroupContinues_ = false;
4319             return;
4320         }
4321 
4322         while(!scopes_.empty() && (!pos_ || pos_.level() >= 1)) {
4323             pos_.undo(scopes_.top());
4324             scopes_.pop();
4325         }
4326         while(!scopes_.empty()) scopes_.pop();
4327 
4328         repeatGroupContinues_ = repeat_group_continues();
4329     }
4330 
4331     //-----------------------------------------------------
4332     void return_to_level(int level)
4333     {
4334         if(pos_.level() <= level) return;
4335         while(!scopes_.empty() && pos_.level() > level) {
4336             pos_.undo(scopes_.top());
4337             scopes_.pop();
4338         }
4339     };
4340 
4341     dfs_traverser pos_;
4342     dfs_traverser lastMatch_;
4343     dfs_traverser posAfterLastMatch_;
4344     std::stack<dfs_traverser::memento> scopes_;
4345     bool ignoreBlocks_ = false;
4346     bool repeatGroupStarted_ = false;
4347     bool repeatGroupContinues_ = false;
4348 };
4349 
4350 
4351 
4352 
4353 /*****************************************************************************
4354  *
4355  * some parameter property predicates
4356  *
4357  *****************************************************************************/
4358 struct select_all {
4359     bool operator () (const parameter&) const noexcept { return true; }
4360 };
4361 
4362 struct select_flags {
4363     bool operator () (const parameter& p) const noexcept {
4364         return !p.flags().empty();
4365     }
4366 };
4367 
4368 struct select_values {
4369     bool operator () (const parameter& p) const noexcept {
4370         return p.flags().empty();
4371     }
4372 };
4373 
4374 
4375 
4376 /*************************************************************************//**
4377  *
4378  * @brief result of a matching operation
4379  *
4380  *****************************************************************************/
4381 class match_t {
4382 public:
4383     using size_type = arg_string::size_type;
4384 
4385     match_t() = default;
4386 
4387     match_t(arg_string s, scoped_dfs_traverser p):
4388         str_{std::move(s)}, pos_{std::move(p)}
4389     {}
4390 
4391     size_type length() const noexcept { return str_.size(); }
4392 
4393     const arg_string& str() const noexcept { return str_; }
4394     const scoped_dfs_traverser& pos() const noexcept { return pos_; }
4395 
4396     explicit operator bool() const noexcept { return bool(pos_); }
4397 
4398 private:
4399     arg_string str_;
4400     scoped_dfs_traverser pos_;
4401 };
4402 
4403 
4404 
4405 /*************************************************************************//**
4406  *
4407  * @brief finds the first parameter that matches a given string;
4408  *        candidate parameters are traversed using a scoped DFS traverser
4409  *
4410  *****************************************************************************/
4411 template<class ParamSelector>
4412 match_t
4413 full_match(scoped_dfs_traverser pos, const arg_string& arg,
4414            const ParamSelector& select)
4415 {
4416     while(pos) {
4417         if(pos->is_param()) {
4418             const auto& param = pos->as_param();
4419             if(select(param)) {
4420                 const auto match = param.match(arg);
4421                 if(match && match.length() == arg.size()) {
4422                     return match_t{arg, std::move(pos)};
4423                 }
4424             }
4425         }
4426         ++pos;
4427     }
4428     return match_t{};
4429 }
4430 
4431 
4432 
4433 /*************************************************************************//**
4434  *
4435  * @brief finds the first parameter that matches any (non-empty) prefix
4436  *        of a given string;
4437  *        candidate parameters are traversed using a scoped DFS traverser
4438  *
4439  *****************************************************************************/
4440 template<class ParamSelector>
4441 match_t
4442 longest_prefix_match(scoped_dfs_traverser pos, const arg_string& arg,
4443                      const ParamSelector& select)
4444 {
4445     match_t longest;
4446 
4447     while(pos) {
4448         if(pos->is_param()) {
4449             const auto& param = pos->as_param();
4450             if(select(param)) {
4451                 auto match = param.match(arg);
4452                 if(match.prefix()) {
4453                     if(match.length() == arg.size()) {
4454                         return match_t{arg, std::move(pos)};
4455                     }
4456                     else if(match.length() > longest.length()) {
4457                         longest = match_t{arg.substr(match.at(), match.length()), 
4458                                           pos};
4459                     }
4460                 }
4461             }
4462         }
4463         ++pos;
4464     }
4465     return longest;
4466 }
4467 
4468 
4469 
4470 /*************************************************************************//**
4471  *
4472  * @brief finds the first parameter that partially matches a given string;
4473  *        candidate parameters are traversed using a scoped DFS traverser
4474  *
4475  *****************************************************************************/
4476 template<class ParamSelector>
4477 match_t
4478 partial_match(scoped_dfs_traverser pos, const arg_string& arg,
4479               const ParamSelector& select)
4480 {
4481     while(pos) {
4482         if(pos->is_param()) {
4483             const auto& param = pos->as_param();
4484             if(select(param)) {
4485                 const auto match = param.match(arg);
4486                 if(match) {
4487                     return match_t{arg.substr(match.at(), match.length()),
4488                                    std::move(pos)};
4489                 }
4490             }
4491         }
4492         ++pos;
4493     }
4494     return match_t{};
4495 }
4496 
4497 } //namespace detail
4498 
4499 
4500 
4501 
4502 
4503 
4504 /***************************************************************//**
4505  *
4506  * @brief default command line arguments parser
4507  *
4508  *******************************************************************/
4509 class parser
4510 {
4511 public:
4512     using dfs_traverser = group::depth_first_traverser;
4513     using scoped_dfs_traverser = detail::scoped_dfs_traverser;
4514 
4515 
4516     /*****************************************************//**
4517      * @brief arg -> parameter mapping
4518      *********************************************************/
4519     class arg_mapping {
4520     public:
4521         friend class parser;
4522 
4523         explicit
4524         arg_mapping(arg_index idx, arg_string s,
4525                     const dfs_traverser& match)
4526         :
4527             index_{idx}, arg_{std::move(s)}, match_{match},
4528             repeat_{0}, startsRepeatGroup_{false},
4529             blocked_{false}, conflict_{false}
4530         {}
4531 
4532         explicit
4533         arg_mapping(arg_index idx, arg_string s) :
4534             index_{idx}, arg_{std::move(s)}, match_{},
4535             repeat_{0}, startsRepeatGroup_{false},
4536             blocked_{false}, conflict_{false}
4537         {}
4538 
4539         arg_index index() const noexcept { return index_; }
4540         const arg_string& arg() const noexcept { return arg_; }
4541 
4542         const parameter* param() const noexcept {
4543             return match_ && match_->is_param()
4544                 ? &(match_->as_param()) : nullptr;
4545         }
4546 
4547         std::size_t repeat() const noexcept { return repeat_; }
4548 
4549         bool blocked() const noexcept { return blocked_; }
4550         bool conflict() const noexcept { return conflict_; }
4551 
4552         bool bad_repeat() const noexcept {
4553             if(!param()) return false;
4554             return repeat_ > 0 && !param()->repeatable()
4555                 && !match_.innermost_repeat_group();
4556         }
4557 
4558         bool any_error() const noexcept {
4559             return !match_ || blocked() || conflict() || bad_repeat();
4560         }
4561 
4562     private:
4563         arg_index index_;
4564         arg_string arg_;
4565         dfs_traverser match_;
4566         std::size_t repeat_;
4567         bool startsRepeatGroup_;
4568         bool blocked_;
4569         bool conflict_;
4570     };
4571 
4572     /*****************************************************//**
4573      * @brief references a non-matched, required parameter
4574      *********************************************************/
4575     class missing_event {
4576     public:
4577         explicit
4578         missing_event(const parameter* p, arg_index after):
4579             param_{p}, aftIndex_{after}
4580         {}
4581 
4582         const parameter* param() const noexcept { return param_; }
4583 
4584         arg_index after_index() const noexcept { return aftIndex_; }
4585 
4586     private:
4587         const parameter* param_;
4588         arg_index aftIndex_;
4589     };
4590 
4591     //-----------------------------------------------------
4592     using missing_events = std::vector<missing_event>;
4593     using arg_mappings   = std::vector<arg_mapping>;
4594 
4595 
4596 private:
4597     struct miss_candidate {
4598         miss_candidate(dfs_traverser p, arg_index idx,
4599                        bool firstInRepeatGroup = false):
4600             pos{std::move(p)}, index{idx},
4601             startsRepeatGroup{firstInRepeatGroup}
4602         {}
4603 
4604         dfs_traverser pos;
4605         arg_index index;
4606         bool startsRepeatGroup;
4607     };
4608     using miss_candidates = std::vector<miss_candidate>;
4609 
4610 
4611 public:
4612     //---------------------------------------------------------------
4613     /** @brief initializes parser with a command line interface
4614      *  @param offset = argument index offset used for reports
4615      * */
4616     explicit
4617     parser(const group& root, arg_index offset = 0):
4618         root_{&root}, pos_{root},
4619         index_{offset-1}, eaten_{0},
4620         args_{}, missCand_{}, blocked_{false}
4621     {
4622         for_each_potential_miss(dfs_traverser{root},
4623             [this](const dfs_traverser& p){
4624                 missCand_.emplace_back(p, index_);
4625             });
4626     }
4627 
4628 
4629     //---------------------------------------------------------------
4630     /** @brief processes one command line argument */
4631     bool operator() (const arg_string& arg)
4632     {
4633         ++eaten_;
4634         ++index_;
4635 
4636         if(!valid()) return false;
4637 
4638         if(!blocked_ && try_match(arg)) return true;
4639 
4640         if(try_match_blocked(arg)) return false;
4641 
4642         //skipping of blocking & required patterns is not allowed
4643         if(!blocked_ && !pos_.matched() && pos_->required() && pos_->blocking()) {
4644             blocked_ = true;
4645         }
4646 
4647         add_nomatch(arg);
4648         return false;
4649     }
4650 
4651 
4652     //---------------------------------------------------------------
4653     /** @brief returns range of argument -> parameter mappings */
4654     const arg_mappings& args() const {
4655         return args_;
4656     }
4657 
4658     /** @brief returns list of missing events */
4659     missing_events missed() const {
4660         missing_events misses;
4661         misses.reserve(missCand_.size());
4662         for(auto i = missCand_.begin(); i != missCand_.end(); ++i) {
4663             misses.emplace_back(&(i->pos->as_param()), i->index);
4664         }
4665         return misses;
4666     }
4667 
4668     /** @brief returns number of processed command line arguments */
4669     arg_index parse_count() const noexcept { return eaten_; }
4670 
4671     /** @brief returns false if previously processed command line arguments
4672      *         lead to an invalid / inconsistent parsing result
4673      */
4674     bool valid() const noexcept { return bool(pos_); }
4675 
4676     /** @brief returns false if previously processed command line arguments
4677      *         lead to an invalid / inconsistent parsing result
4678      */
4679     explicit operator bool() const noexcept { return valid(); }
4680 
4681 
4682 private:
4683     //---------------------------------------------------------------
4684     using match_t = detail::match_t;
4685 
4686 
4687     //---------------------------------------------------------------
4688     /** @brief try to match argument with unreachable parameter */
4689     bool try_match_blocked(const arg_string& arg)
4690     {
4691         //try to match ahead (using temporary parser)
4692         if(pos_) {
4693             auto ahead = *this;
4694             if(try_match_blocked(std::move(ahead), arg)) return true;
4695         }
4696 
4697         //try to match from the beginning (using temporary parser)
4698         if(root_) {
4699             parser all{*root_, index_+1};
4700             if(try_match_blocked(std::move(all), arg)) return true;
4701         }
4702 
4703         return false;
4704     }
4705 
4706     //---------------------------------------------------------------
4707     bool try_match_blocked(parser&& parse, const arg_string& arg)
4708     {
4709         const auto nold = int(parse.args_.size());
4710 
4711         parse.pos_.ignore_blocking(true);
4712 
4713         if(!parse.try_match(arg)) return false;
4714 
4715         for(auto i = parse.args_.begin() + nold; i != parse.args_.end(); ++i) {
4716             args_.push_back(*i);
4717             args_.back().blocked_ = true;
4718         }
4719         return true;
4720     }
4721 
4722     //---------------------------------------------------------------
4723     /** @brief try to find a parameter/pattern that matches 'arg' */
4724     bool try_match(const arg_string& arg)
4725     {
4726         //match greedy parameters before everything else
4727         if(pos_->is_param() && pos_->blocking() && pos_->as_param().greedy()) {
4728             const auto match = pos_->as_param().match(arg);
4729             if(match && match.length() == arg.size()) {
4730                 add_match(detail::match_t{arg,pos_});
4731                 return true;
4732             }
4733         }
4734 
4735         //try flags first (alone, joinable or strict sequence)
4736         if(try_match_full(arg, detail::select_flags{})) return true;
4737         if(try_match_joined_flags(arg)) return true;
4738         if(try_match_joined_sequence(arg, detail::select_flags{})) return true;
4739         //try value params (alone or strict sequence)
4740         if(try_match_full(arg, detail::select_values{})) return true;
4741         if(try_match_joined_sequence(arg, detail::select_all{})) return true;
4742         //try joinable params + values in any order
4743         if(try_match_joined_params(arg)) return true;
4744         return false;
4745     }
4746 
4747     //---------------------------------------------------------------
4748     /**
4749      * @brief try to match full argument
4750      * @param select : predicate that candidate parameters must satisfy
4751      */
4752     template<class ParamSelector>
4753     bool try_match_full(const arg_string& arg, const ParamSelector& select)
4754     {
4755         auto match = detail::full_match(pos_, arg, select);
4756         if(!match) return false;
4757         add_match(match);
4758         return true;
4759     }
4760 
4761     //---------------------------------------------------------------
4762     /**
4763      * @brief try to match argument as blocking sequence of parameters
4764      * @param select : predicate that a parameter matching the prefix of
4765      *                 'arg' must satisfy
4766      */
4767     template<class ParamSelector>
4768     bool try_match_joined_sequence(arg_string arg,
4769                                    const ParamSelector& acceptFirst)
4770     {
4771         auto fstMatch = detail::longest_prefix_match(pos_, arg, acceptFirst);
4772 
4773         if(!fstMatch) return false;
4774 
4775         if(fstMatch.str().size() == arg.size()) {
4776             add_match(fstMatch);
4777             return true;
4778         }
4779 
4780         if(!fstMatch.pos()->blocking()) return false;
4781 
4782         auto pos = fstMatch.pos();
4783         pos.ignore_blocking(true);
4784         const auto parent = &pos.parent();
4785         if(!pos->repeatable()) ++pos;
4786 
4787         arg.erase(0, fstMatch.str().size());
4788         std::vector<match_t> matches { std::move(fstMatch) };
4789 
4790         while(!arg.empty() && pos &&
4791               pos->blocking() && pos->is_param() &&
4792               (&pos.parent() == parent))
4793         {
4794             auto match = pos->as_param().match(arg);
4795 
4796             if(match.prefix()) {
4797                 matches.emplace_back(arg.substr(0,match.length()), pos);
4798                 arg.erase(0, match.length());
4799                 if(!pos->repeatable()) ++pos;
4800             }
4801             else {
4802                 if(!pos->repeatable()) return false;
4803                 ++pos;
4804             }
4805 
4806         }
4807         //if arg not fully covered => discard temporary matches
4808         if(!arg.empty() || matches.empty()) return false;
4809 
4810         for(const auto& m : matches) add_match(m);
4811         return true;
4812     }
4813 
4814     //-----------------------------------------------------
4815     /** @brief try to match 'arg' as a concatenation of joinable flags */
4816     bool try_match_joined_flags(const arg_string& arg)
4817     {
4818         return find_join_group(pos_, [&](const group& g) {
4819             return try_match_joined(g, arg, detail::select_flags{},
4820                                     g.common_flag_prefix());
4821         });
4822     }
4823 
4824     //---------------------------------------------------------------
4825     /** @brief try to match 'arg' as a concatenation of joinable parameters */
4826     bool try_match_joined_params(const arg_string& arg)
4827     {
4828         return find_join_group(pos_, [&](const group& g) {
4829             return try_match_joined(g, arg, detail::select_all{});
4830         });
4831     }
4832 
4833     //-----------------------------------------------------
4834     /** @brief try to match 'arg' as concatenation of joinable parameters
4835      *         that are all contained within one group
4836      */
4837     template<class ParamSelector>
4838     bool try_match_joined(const group& joinGroup, arg_string arg,
4839                           const ParamSelector& select,
4840                           const arg_string& prefix = "")
4841     {
4842         //temporary parser with 'joinGroup' as top-level group
4843         parser parse {joinGroup};
4844         //records temporary matches
4845         std::vector<match_t> matches;
4846 
4847         while(!arg.empty()) {
4848             auto match = detail::longest_prefix_match(parse.pos_, arg, select);
4849 
4850             if(!match) return false;
4851 
4852             arg.erase(0, match.str().size());
4853             //make sure prefix is always present after the first match
4854             //so that, e.g., flags "-a" and "-b" will be found in "-ab"
4855             if(!arg.empty() && !prefix.empty() && arg.find(prefix) != 0 &&
4856                 prefix != match.str())
4857             {
4858                 arg.insert(0,prefix);
4859             }
4860 
4861             parse.add_match(match);
4862             matches.push_back(std::move(match));
4863         }
4864 
4865         if(!arg.empty() || matches.empty()) return false;
4866 
4867         if(!parse.missCand_.empty()) return false;
4868         for(const auto& a : parse.args_) if(a.any_error()) return false;
4869 
4870         //replay matches onto *this
4871         for(const auto& m : matches) add_match(m);
4872         return true;
4873     }
4874 
4875     //-----------------------------------------------------
4876     template<class GroupSelector>
4877     bool find_join_group(const scoped_dfs_traverser& start,
4878                          const GroupSelector& accept) const
4879     {
4880         if(start && start.parent().joinable()) {
4881             const auto& g = start.parent();
4882             if(accept(g)) return true;
4883             return false;
4884         }
4885 
4886         auto pos = start;
4887         while(pos) {
4888             if(pos->is_group() && pos->as_group().joinable()) {
4889                 const auto& g = pos->as_group();
4890                 if(accept(g)) return true;
4891                 pos.next_sibling();
4892             }
4893             else {
4894                 ++pos;
4895             }
4896         }
4897         return false;
4898     }
4899 
4900 
4901     //---------------------------------------------------------------
4902     void add_nomatch(const arg_string& arg) {
4903         args_.emplace_back(index_, arg);
4904     }
4905 
4906 
4907     //---------------------------------------------------------------
4908     void add_match(const match_t& match)
4909     {
4910         const auto& pos = match.pos();
4911         if(!pos || !pos->is_param()) return;
4912 
4913         pos_.next_after_match(pos);
4914 
4915         arg_mapping newArg{index_, match.str(), pos.base()};
4916         newArg.repeat_ = occurrences_of(&pos->as_param());
4917         newArg.conflict_ = check_conflicts(pos.base());
4918         newArg.startsRepeatGroup_ = pos_.start_of_repeat_group();
4919         args_.push_back(std::move(newArg));
4920 
4921         add_miss_candidates_after(pos);
4922         clean_miss_candidates_for(pos.base());
4923         discard_alternative_miss_candidates(pos.base());
4924 
4925     }
4926 
4927     //-----------------------------------------------------
4928     bool check_conflicts(const dfs_traverser& match)
4929     {
4930         if(pos_.start_of_repeat_group()) return false;
4931         bool conflict = false;
4932         for(const auto& m : match.stack()) {
4933             if(m.parent->exclusive()) {
4934                 for(auto i = args_.rbegin(); i != args_.rend(); ++i) {
4935                     if(!i->blocked()) {
4936                         for(const auto& c : i->match_.stack()) {
4937                             //sibling within same exclusive group => conflict
4938                             if(c.parent == m.parent && c.cur != m.cur) {
4939                                 conflict = true;
4940                                 i->conflict_ = true;
4941                             }
4942                         }
4943                     }
4944                     //check for conflicts only within current repeat cycle
4945                     if(i->startsRepeatGroup_) break;
4946                 }
4947             }
4948         }
4949         return conflict;
4950     }
4951 
4952     //-----------------------------------------------------
4953     void clean_miss_candidates_for(const dfs_traverser& match)
4954     {
4955         auto i = std::find_if(missCand_.rbegin(), missCand_.rend(),
4956             [&](const miss_candidate& m) {
4957                 return &(*m.pos) == &(*match);
4958             });
4959 
4960         if(i != missCand_.rend()) {
4961             missCand_.erase(prev(i.base()));
4962         }
4963     }
4964 
4965     //-----------------------------------------------------
4966     void discard_alternative_miss_candidates(const dfs_traverser& match)
4967     {
4968         if(missCand_.empty()) return;
4969         //find out, if miss candidate is sibling of one of the same
4970         //alternative groups that the current match is a member of
4971         //if so, we can discard the miss
4972 
4973         //go through all exclusive groups of matching pattern
4974         for(const auto& m : match.stack()) {
4975             if(m.parent->exclusive()) {
4976                 for(auto i = int(missCand_.size())-1; i >= 0; --i) {
4977                     bool removed = false;
4978                     for(const auto& c : missCand_[i].pos.stack()) {
4979                         //sibling within same exclusive group => discard
4980                         if(c.parent == m.parent && c.cur != m.cur) {
4981                             missCand_.erase(missCand_.begin() + i);
4982                             if(missCand_.empty()) return;
4983                             removed = true;
4984                             break;
4985                         }
4986                     }
4987                     //remove miss candidates only within current repeat cycle
4988                     if(i > 0 && removed) {
4989                         if(missCand_[i-1].startsRepeatGroup) break;
4990                     } else {
4991                         if(missCand_[i].startsRepeatGroup) break;
4992                     }
4993                 }
4994             }
4995         }
4996     }
4997 
4998     //-----------------------------------------------------
4999     void add_miss_candidates_after(const scoped_dfs_traverser& match)
5000     {
5001         auto npos = match.base();
5002         if(npos.is_alternative()) npos.skip_alternatives();
5003         ++npos;
5004         //need to add potential misses if:
5005         //either new repeat group was started
5006         const auto newRepGroup = match.innermost_repeat_group();
5007         if(newRepGroup) {
5008             if(pos_.start_of_repeat_group()) {
5009                 for_each_potential_miss(std::move(npos),
5010                     [&,this](const dfs_traverser& pos) {
5011                         //only add candidates within repeat group
5012                         if(newRepGroup == pos.innermost_repeat_group()) {
5013                             missCand_.emplace_back(pos, index_, true);
5014                         }
5015                     });
5016             }
5017         }
5018         //... or an optional blocking param was hit
5019         else if(match->blocking() && !match->required() &&
5020             npos.level() >= match.base().level())
5021         {
5022             for_each_potential_miss(std::move(npos),
5023                 [&,this](const dfs_traverser& pos) {
5024                     //only add new candidates
5025                     if(std::find_if(missCand_.begin(), missCand_.end(),
5026                         [&](const miss_candidate& c){
5027                             return &(*c.pos) == &(*pos);
5028                         }) == missCand_.end())
5029                     {
5030                         missCand_.emplace_back(pos, index_);
5031                     }
5032                 });
5033         }
5034 
5035     }
5036 
5037     //-----------------------------------------------------
5038     template<class Action>
5039     static void
5040     for_each_potential_miss(dfs_traverser pos, Action&& action)
5041     {
5042         const auto level = pos.level();
5043         while(pos && pos.level() >= level) {
5044             if(pos->is_group() ) {
5045                 const auto& g = pos->as_group();
5046                 if(g.all_optional() || (g.exclusive() && g.any_optional())) {
5047                     pos.next_sibling();
5048                 } else {
5049                     ++pos;
5050                 }
5051             } else {  //param
5052                 if(pos->required()) {
5053                     action(pos);
5054                     ++pos;
5055                 } else if(pos->blocking()) { //optional + blocking
5056                     pos.next_after_siblings();
5057                 } else {
5058                     ++pos;
5059                 }
5060             }
5061         }
5062     }
5063 
5064 
5065     //---------------------------------------------------------------
5066     std::size_t occurrences_of(const parameter* p) const
5067     {
5068         if(!p) return 0;
5069 
5070         auto i = std::find_if(args_.rbegin(), args_.rend(),
5071             [p](const arg_mapping& a){ return a.param() == p; });
5072 
5073         if(i != args_.rend()) return i->repeat() + 1;
5074         return 0;
5075     }
5076 
5077 
5078     //---------------------------------------------------------------
5079     const group* root_;
5080     scoped_dfs_traverser pos_;
5081     arg_index index_;
5082     arg_index eaten_;
5083     arg_mappings args_;
5084     miss_candidates missCand_;
5085     bool blocked_;
5086 };
5087 
5088 
5089 
5090 
5091 /*************************************************************************//**
5092  *
5093  * @brief contains argument -> parameter mappings
5094  *        and missing parameters
5095  *
5096  *****************************************************************************/
5097 class parsing_result
5098 {
5099 public:
5100     using arg_mapping    = parser::arg_mapping;
5101     using arg_mappings   = parser::arg_mappings;
5102     using missing_event  = parser::missing_event;
5103     using missing_events = parser::missing_events;
5104     using iterator       = arg_mappings::const_iterator;
5105 
5106     //-----------------------------------------------------
5107     /** @brief default: empty result */
5108     parsing_result() = default;
5109 
5110     parsing_result(arg_mappings arg2param, missing_events misses):
5111         arg2param_{std::move(arg2param)}, missing_{std::move(misses)}
5112     {}
5113 
5114     //-----------------------------------------------------
5115     /** @brief returns number of arguments that could not be mapped to
5116      *         a parameter
5117      */
5118     arg_mappings::size_type
5119     unmapped_args_count() const noexcept {
5120         return std::count_if(arg2param_.begin(), arg2param_.end(),
5121             [](const arg_mapping& a){ return !a.param(); });
5122     }
5123 
5124     /** @brief returns if any argument could only be matched by an
5125      *         unreachable parameter
5126      */
5127     bool any_blocked() const noexcept {
5128         return std::any_of(arg2param_.begin(), arg2param_.end(),
5129             [](const arg_mapping& a){ return a.blocked(); });
5130     }
5131 
5132     /** @brief returns if any argument matched more than one parameter
5133      *         that were mutually exclusive */
5134     bool any_conflict() const noexcept {
5135         return std::any_of(arg2param_.begin(), arg2param_.end(),
5136             [](const arg_mapping& a){ return a.conflict(); });
5137     }
5138 
5139     /** @brief returns if any parameter matched repeatedly although
5140      *         it was not allowed to */
5141     bool any_bad_repeat() const noexcept {
5142         return std::any_of(arg2param_.begin(), arg2param_.end(),
5143             [](const arg_mapping& a){ return a.bad_repeat(); });
5144     }
5145 
5146     /** @brief returns true if any parsing error / violation of the
5147      *         command line interface definition occurred */
5148     bool any_error() const noexcept {
5149         return unmapped_args_count() > 0 || !missing().empty() ||
5150                any_blocked() || any_conflict() || any_bad_repeat();
5151     }
5152 
5153     /** @brief returns true if no parsing error / violation of the
5154      *         command line interface definition occurred */
5155     explicit operator bool() const noexcept { return !any_error(); }
5156 
5157     /** @brief access to range of missing parameter match events */
5158     const missing_events& missing() const noexcept { return missing_; }
5159 
5160     /** @brief returns non-mutating iterator to position of
5161      *         first argument -> parameter mapping  */
5162     iterator begin() const noexcept { return arg2param_.begin(); }
5163     /** @brief returns non-mutating iterator to position one past the
5164      *         last argument -> parameter mapping  */
5165     iterator end()   const noexcept { return arg2param_.end(); }
5166 
5167 private:
5168     //-----------------------------------------------------
5169     arg_mappings arg2param_;
5170     missing_events missing_;
5171 };
5172 
5173 
5174 
5175 
5176 namespace detail {
5177 namespace {
5178 
5179 /*************************************************************************//**
5180  *
5181  * @brief correct some common problems
5182  *        does not - and MUST NOT - change the number of arguments
5183  *        (no insertions or deletions allowed)
5184  *
5185  *****************************************************************************/
5186 void sanitize_args(arg_list& args)
5187 {
5188     //e.g. {"-o12", ".34"} -> {"-o", "12.34"}
5189 
5190     if(args.empty()) return;
5191 
5192     for(auto i = begin(args)+1; i != end(args); ++i) {
5193         if(i != begin(args) && i->size() > 1 &&
5194             i->find('.') == 0 && std::isdigit((*i)[1]) )
5195         {
5196             //find trailing digits in previous arg
5197             using std::prev;
5198             auto& prv = *prev(i);
5199             auto fstDigit = std::find_if_not(prv.rbegin(), prv.rend(),
5200                 [](arg_string::value_type c){
5201                     return std::isdigit(c);
5202                 }).base();
5203 
5204             //handle leading sign
5205             if(fstDigit > prv.begin() &&
5206                 (*prev(fstDigit) == '+' || *prev(fstDigit) == '-'))
5207             {
5208                 --fstDigit;
5209             }
5210 
5211             //prepend digits from previous arg
5212             i->insert(begin(*i), fstDigit, end(prv));
5213 
5214             //erase digits in previous arg
5215             prv.erase(fstDigit, end(prv));
5216         }
5217     }
5218 }
5219 
5220 
5221 
5222 /*************************************************************************//**
5223  *
5224  * @brief executes actions based on a parsing result
5225  *
5226  *****************************************************************************/
5227 void execute_actions(const parsing_result& res)
5228 {
5229     for(const auto& m : res) {
5230         if(m.param()) {
5231             const auto& param = *(m.param());
5232 
5233             if(m.repeat() > 0) param.notify_repeated(m.index());
5234             if(m.blocked())    param.notify_blocked(m.index());
5235             if(m.conflict())   param.notify_conflict(m.index());
5236             //main action
5237             if(!m.any_error()) param.execute_actions(m.arg());
5238         }
5239     }
5240 
5241     for(auto m : res.missing()) {
5242         if(m.param()) m.param()->notify_missing(m.after_index());
5243     }
5244 }
5245 
5246 
5247 
5248 /*************************************************************************//**
5249  *
5250  * @brief parses input args
5251  *
5252  *****************************************************************************/
5253 static parsing_result
5254 parse_args(const arg_list& args, const group& cli,
5255            arg_index offset = 0)
5256 {
5257     //parse args and store unrecognized arg indices
5258     parser parse{cli, offset};
5259     for(const auto& arg : args) {
5260         parse(arg);
5261         if(!parse.valid()) break;
5262     }
5263 
5264     return parsing_result{parse.args(), parse.missed()};
5265 }
5266 
5267 /*************************************************************************//**
5268  *
5269  * @brief parses input args & executes actions
5270  *
5271  *****************************************************************************/
5272 static parsing_result
5273 parse_and_execute(const arg_list& args, const group& cli,
5274                   arg_index offset = 0)
5275 {
5276     auto result = parse_args(args, cli, offset);
5277 
5278     execute_actions(result);
5279 
5280     return result;
5281 }
5282 
5283 } //anonymous namespace
5284 } // namespace detail
5285 
5286 
5287 
5288 
5289 /*************************************************************************//**
5290  *
5291  * @brief parses vector of arg strings and executes actions
5292  *
5293  *****************************************************************************/
5294 inline parsing_result
5295 parse(arg_list args, const group& cli)
5296 {
5297     detail::sanitize_args(args);
5298     return detail::parse_and_execute(args, cli);
5299 }
5300 
5301 
5302 /*************************************************************************//**
5303  *
5304  * @brief parses initializer_list of C-style arg strings and executes actions
5305  *
5306  *****************************************************************************/
5307 inline parsing_result
5308 parse(std::initializer_list<const char*> arglist, const group& cli)
5309 {
5310     arg_list args;
5311     args.reserve(arglist.size());
5312     for(auto a : arglist) {
5313         args.push_back(a);
5314     }
5315 
5316     return parse(std::move(args), cli);
5317 }
5318 
5319 
5320 /*************************************************************************//**
5321  *
5322  * @brief parses range of arg strings and executes actions
5323  *
5324  *****************************************************************************/
5325 template<class InputIterator>
5326 inline parsing_result
5327 parse(InputIterator first, InputIterator last, const group& cli)
5328 {
5329     return parse(arg_list(first,last), cli);
5330 }
5331 
5332 
5333 /*************************************************************************//**
5334  *
5335  * @brief parses the standard array of command line arguments; omits argv[0]
5336  *
5337  *****************************************************************************/
5338 inline parsing_result
5339 parse(const int argc, char* argv[], const group& cli, arg_index offset = 1)
5340 {
5341     arg_list args;
5342     if(offset < argc) args.assign(argv+offset, argv+argc);
5343     detail::sanitize_args(args);
5344     return detail::parse_and_execute(args, cli, offset);
5345 }
5346 
5347 
5348 
5349 
5350 
5351 
5352 /*************************************************************************//**
5353  *
5354  * @brief filter predicate for parameters and groups;
5355  *        Can be used to limit documentation generation to parameter subsets.
5356  *
5357  *****************************************************************************/
5358 class param_filter
5359 {
5360 public:
5361     /** @brief only allow parameters with given prefix */
5362     param_filter& prefix(const arg_string& p) noexcept {
5363         prefix_ = p; return *this;
5364     }
5365     /** @brief only allow parameters with given prefix */
5366     param_filter& prefix(arg_string&& p) noexcept {
5367         prefix_ = std::move(p); return *this;
5368     }
5369     const arg_string& prefix()  const noexcept { return prefix_; }
5370 
5371     /** @brief only allow parameters with given requirement status */
5372     param_filter& required(tri t)  noexcept { required_ = t; return *this; }
5373     tri           required() const noexcept { return required_; }
5374 
5375     /** @brief only allow parameters with given blocking status */
5376     param_filter& blocking(tri t)  noexcept { blocking_ = t; return *this; }
5377     tri           blocking() const noexcept { return blocking_; }
5378 
5379     /** @brief only allow parameters with given repeatable status */
5380     param_filter& repeatable(tri t)  noexcept { repeatable_ = t; return *this; }
5381     tri           repeatable() const noexcept { return repeatable_; }
5382 
5383     /** @brief only allow parameters with given docstring status */
5384     param_filter& has_doc(tri t)  noexcept { hasDoc_ = t; return *this; }
5385     tri           has_doc() const noexcept { return hasDoc_; }
5386 
5387 
5388     /** @brief returns true, if parameter satisfies all filters */
5389     bool operator() (const parameter& p) const noexcept {
5390         if(!prefix_.empty()) {
5391             if(!std::any_of(p.flags().begin(), p.flags().end(),
5392                 [&](const arg_string& flag){
5393                     return str::has_prefix(flag, prefix_);
5394                 })) return false;
5395         }
5396         if(required()   != p.required())     return false;
5397         if(blocking()   != p.blocking())     return false;
5398         if(repeatable() != p.repeatable())   return false;
5399         if(has_doc()    != !p.doc().empty()) return false;
5400         return true;
5401     }
5402 
5403 private:
5404     arg_string prefix_;
5405     tri required_   = tri::either;
5406     tri blocking_   = tri::either;
5407     tri repeatable_ = tri::either;
5408     tri hasDoc_     = tri::yes;
5409 };
5410 
5411 
5412 
5413 
5414 
5415 
5416 /*************************************************************************//**
5417  *
5418  * @brief documentation formatting options
5419  *
5420  *****************************************************************************/
5421 class doc_formatting
5422 {
5423 public:
5424     using string = doc_string;
5425 
5426     /** @brief same as 'first_column' */
5427 #if __cplusplus >= 201402L
5428     [[deprecated]]
5429 #endif
5430     doc_formatting& start_column(int col) { return first_column(col); }
5431 #if __cplusplus >= 201402L
5432     [[deprecated]]
5433 #endif
5434     int start_column() const noexcept { return first_column(); }
5435 
5436     /** @brief determines column where documentation printing starts */
5437     doc_formatting&
5438     first_column(int col) {
5439         //limit to [0,last_column] but push doc_column to the right if necessary
5440         if(col < 0) col = 0;
5441         else if(col > last_column()) col = last_column();
5442         if(col > doc_column()) doc_column(first_column());
5443         firstCol_ = col;
5444         return *this;
5445     }
5446     int first_column() const noexcept {
5447         return firstCol_;
5448     }
5449 
5450     /** @brief determines column where docstrings start */
5451     doc_formatting&
5452     doc_column(int col) {
5453         //limit to [first_column,last_column]
5454         if(col < 0) col = 0;
5455         else if(col < first_column()) col = first_column();
5456         else if(col > last_column()) col = last_column();
5457         docCol_ = col;
5458         return *this;
5459     }
5460     int doc_column() const noexcept {
5461         return docCol_;
5462     }
5463 
5464     /** @brief determines column that no documentation text must exceed;
5465      *         (text should be wrapped appropriately after this column)
5466      */
5467     doc_formatting&
5468     last_column(int col) {
5469         //limit to [first_column,oo] but push doc_column to the left if necessary
5470         if(col < first_column()) col = first_column();
5471         if(col < doc_column()) doc_column(col);
5472         lastCol_ = col;
5473         return *this;
5474     }
5475 
5476     int last_column() const noexcept {
5477         return lastCol_;
5478     }
5479 
5480     /** @brief determines indent of documentation lines
5481      *         for children of a documented group */
5482     doc_formatting& indent_size(int indent) { indentSize_ = indent; return *this; }
5483     int             indent_size() const noexcept  { return indentSize_; }
5484 
5485     /** @brief determines string to be used
5486      *         if a parameter has no flags and no label  */
5487     doc_formatting& empty_label(const string& label) {
5488         emptyLabel_ = label;
5489         return *this;
5490     }
5491     const string& empty_label() const noexcept { return emptyLabel_; }
5492 
5493     /** @brief determines string for separating parameters */
5494     doc_formatting& param_separator(const string& sep) {
5495         paramSep_ = sep;
5496         return *this;
5497     }
5498     const string& param_separator() const noexcept { return paramSep_; }
5499 
5500     /** @brief determines string for separating groups (in usage lines) */
5501     doc_formatting& group_separator(const string& sep) {
5502         groupSep_ = sep;
5503         return *this;
5504     }
5505     const string& group_separator() const noexcept { return groupSep_; }
5506 
5507     /** @brief determines string for separating alternative parameters */
5508     doc_formatting& alternative_param_separator(const string& sep) {
5509         altParamSep_ = sep;
5510         return *this;
5511     }
5512     const string& alternative_param_separator() const noexcept { return altParamSep_; }
5513 
5514     /** @brief determines string for separating alternative groups */
5515     doc_formatting& alternative_group_separator(const string& sep) {
5516         altGroupSep_ = sep;
5517         return *this;
5518     }
5519     const string& alternative_group_separator() const noexcept { return altGroupSep_; }
5520 
5521     /** @brief determines string for separating flags of the same parameter */
5522     doc_formatting& flag_separator(const string& sep) {
5523         flagSep_ = sep;
5524         return *this;
5525     }
5526     const string& flag_separator() const noexcept { return flagSep_; }
5527 
5528     /** @brief determines strings surrounding parameter labels */
5529     doc_formatting&
5530     surround_labels(const string& prefix, const string& postfix) {
5531         labelPre_ = prefix;
5532         labelPst_ = postfix;
5533         return *this;
5534     }
5535     const string& label_prefix()  const noexcept { return labelPre_; }
5536     const string& label_postfix() const noexcept { return labelPst_; }
5537 
5538     /** @brief determines strings surrounding optional parameters/groups */
5539     doc_formatting&
5540     surround_optional(const string& prefix, const string& postfix) {
5541         optionPre_ = prefix;
5542         optionPst_ = postfix;
5543         return *this;
5544     }
5545     const string& optional_prefix()  const noexcept { return optionPre_; }
5546     const string& optional_postfix() const noexcept { return optionPst_; }
5547 
5548     /** @brief determines strings surrounding repeatable parameters/groups */
5549     doc_formatting&
5550     surround_repeat(const string& prefix, const string& postfix) {
5551         repeatPre_ = prefix;
5552         repeatPst_ = postfix;
5553         return *this;
5554     }
5555     const string& repeat_prefix()  const noexcept { return repeatPre_; }
5556     const string& repeat_postfix() const noexcept { return repeatPst_; }
5557 
5558     /** @brief determines strings surrounding exclusive groups */
5559     doc_formatting&
5560     surround_alternatives(const string& prefix, const string& postfix) {
5561         alternPre_ = prefix;
5562         alternPst_ = postfix;
5563         return *this;
5564     }
5565     const string& alternatives_prefix()  const noexcept { return alternPre_; }
5566     const string& alternatives_postfix() const noexcept { return alternPst_; }
5567 
5568     /** @brief determines strings surrounding alternative flags */
5569     doc_formatting&
5570     surround_alternative_flags(const string& prefix, const string& postfix) {
5571         alternFlagPre_ = prefix;
5572         alternFlagPst_ = postfix;
5573         return *this;
5574     }
5575     const string& alternative_flags_prefix()  const noexcept { return alternFlagPre_; }
5576     const string& alternative_flags_postfix() const noexcept { return alternFlagPst_; }
5577 
5578     /** @brief determines strings surrounding non-exclusive groups */
5579     doc_formatting&
5580     surround_group(const string& prefix, const string& postfix) {
5581         groupPre_ = prefix;
5582         groupPst_ = postfix;
5583         return *this;
5584     }
5585     const string& group_prefix()  const noexcept { return groupPre_; }
5586     const string& group_postfix() const noexcept { return groupPst_; }
5587 
5588     /** @brief determines strings surrounding joinable groups */
5589     doc_formatting&
5590     surround_joinable(const string& prefix, const string& postfix) {
5591         joinablePre_ = prefix;
5592         joinablePst_ = postfix;
5593         return *this;
5594     }
5595     const string& joinable_prefix()  const noexcept { return joinablePre_; }
5596     const string& joinable_postfix() const noexcept { return joinablePst_; }
5597 
5598     /** @brief determines maximum number of flags per parameter to be printed
5599      *         in detailed parameter documentation lines */
5600     doc_formatting& max_flags_per_param_in_doc(int max) {
5601         maxAltInDocs_ = max > 0 ? max : 0;
5602         return *this;
5603     }
5604     int max_flags_per_param_in_doc() const noexcept { return maxAltInDocs_; }
5605 
5606     /** @brief determines maximum number of flags per parameter to be printed
5607      *         in usage lines */
5608     doc_formatting& max_flags_per_param_in_usage(int max) {
5609         maxAltInUsage_ = max > 0 ? max : 0;
5610         return *this;
5611     }
5612     int max_flags_per_param_in_usage() const noexcept { return maxAltInUsage_; }
5613 
5614     /** @brief determines number of empty rows after one single-line
5615      *         documentation entry */
5616     doc_formatting& line_spacing(int lines) {
5617         lineSpc_ = lines > 0 ? lines : 0;
5618         return *this;
5619     }
5620     int line_spacing() const noexcept { return lineSpc_; }
5621 
5622     /** @brief determines number of empty rows before and after a paragraph;
5623      *         a paragraph is defined by a documented group or if
5624      *         a parameter documentation entry used more than one line */
5625     doc_formatting& paragraph_spacing(int lines) {
5626         paragraphSpc_ = lines > 0 ? lines : 0;
5627         return *this;
5628     }
5629     int paragraph_spacing() const noexcept { return paragraphSpc_; }
5630 
5631     /** @brief determines if alternative flags with a common prefix should
5632      *         be printed in a merged fashion */
5633     doc_formatting& merge_alternative_flags_with_common_prefix(bool yes = true) {
5634         mergeAltCommonPfx_ = yes;
5635         return *this;
5636     }
5637     bool merge_alternative_flags_with_common_prefix() const noexcept {
5638         return mergeAltCommonPfx_;
5639     }
5640 
5641     /** @brief determines if joinable flags with a common prefix should
5642      *         be printed in a merged fashion */
5643     doc_formatting& merge_joinable_with_common_prefix(bool yes = true) {
5644         mergeJoinableCommonPfx_ = yes;
5645         return *this;
5646     }
5647     bool merge_joinable_with_common_prefix() const noexcept {
5648         return mergeJoinableCommonPfx_;
5649     }
5650 
5651     /** @brief determines if children of exclusive groups should be printed
5652      *         on individual lines if the exceed 'alternatives_min_split_size'
5653      */
5654     doc_formatting& split_alternatives(bool yes = true) {
5655         splitTopAlt_ = yes;
5656         return *this;
5657     }
5658     bool split_alternatives() const noexcept {
5659         return splitTopAlt_;
5660     }
5661 
5662     /** @brief determines how many children exclusive groups can have before
5663      *         their children are printed on individual usage lines */
5664     doc_formatting& alternatives_min_split_size(int size) {
5665         groupSplitSize_ = size > 0 ? size : 0;
5666         return *this;
5667     }
5668     int alternatives_min_split_size() const noexcept { return groupSplitSize_; }
5669 
5670     /** @brief determines whether to ignore new line characters in docstrings
5671      */
5672     doc_formatting& ignore_newline_chars(bool yes = true) {
5673         ignoreNewlines_ = yes;
5674         return *this;
5675     }
5676     bool ignore_newline_chars() const noexcept {
5677         return ignoreNewlines_;
5678     }
5679 
5680 private:
5681     string paramSep_      = string(" ");
5682     string groupSep_      = string(" ");
5683     string altParamSep_   = string("|");
5684     string altGroupSep_   = string(" | ");
5685     string flagSep_       = string(", ");
5686     string labelPre_      = string("<");
5687     string labelPst_      = string(">");
5688     string optionPre_     = string("[");
5689     string optionPst_     = string("]");
5690     string repeatPre_     = string("");
5691     string repeatPst_     = string("...");
5692     string groupPre_      = string("(");
5693     string groupPst_      = string(")");
5694     string alternPre_     = string("(");
5695     string alternPst_     = string(")");
5696     string alternFlagPre_ = string("");
5697     string alternFlagPst_ = string("");
5698     string joinablePre_   = string("(");
5699     string joinablePst_   = string(")");
5700     string emptyLabel_    = string("");
5701     int firstCol_ = 8;
5702     int docCol_ = 20;
5703     int lastCol_ = 100;
5704     int indentSize_ = 4;
5705     int maxAltInUsage_ = 1;
5706     int maxAltInDocs_ = 32;
5707     int lineSpc_ = 0;
5708     int paragraphSpc_ = 1;
5709     int groupSplitSize_ = 3;
5710     bool splitTopAlt_ = true;
5711     bool mergeAltCommonPfx_ = false;
5712     bool mergeJoinableCommonPfx_ = true;
5713     bool ignoreNewlines_ = false;
5714 };
5715 
5716 
5717 
5718 namespace detail {
5719 
5720 /*************************************************************************//**
5721  *
5722  * @brief stream decorator
5723  *        that applies formatting like line wrapping
5724  *
5725  *****************************************************************************/
5726 template<class OStream = std::ostream, class StringT = doc_string>
5727 class formatting_ostream
5728 {
5729 public:
5730     using string_type = StringT;
5731     using size_type   = typename string_type::size_type;
5732     using char_type   = typename string_type::value_type;
5733 
5734     formatting_ostream(OStream& os):
5735         os_(os),
5736         curCol_{0}, firstCol_{0}, lastCol_{100},
5737         hangingIndent_{0}, paragraphSpacing_{0}, paragraphSpacingThreshold_{2},
5738         curBlankLines_{0}, curParagraphLines_{1},
5739         totalNonBlankLines_{0},
5740         ignoreInputNls_{false}
5741     {}
5742 
5743 
5744     //---------------------------------------------------------------
5745     const OStream& base() const noexcept { return os_; }
5746           OStream& base()       noexcept { return os_; }
5747 
5748     bool good() const { return os_.good(); }
5749 
5750 
5751     //---------------------------------------------------------------
5752     /** @brief determines the leftmost border of the text body */
5753     formatting_ostream& first_column(int c) {
5754         firstCol_ = c < 0 ? 0 : c;
5755         return *this;
5756     }
5757     int first_column() const noexcept { return firstCol_; }
5758 
5759     /** @brief determines the rightmost border of the text body */
5760     formatting_ostream& last_column(int c) {
5761         lastCol_ = c < 0 ? 0 : c;
5762         return *this;
5763     }
5764 
5765     int last_column() const noexcept { return lastCol_; }
5766 
5767     int text_width() const noexcept {
5768         return lastCol_ - firstCol_;
5769     }
5770 
5771     /** @brief additional indentation for the 2nd, 3rd, ... line of
5772                a paragraph (sequence of soft-wrapped lines) */
5773     formatting_ostream& hanging_indent(int amount) {
5774         hangingIndent_ = amount;
5775         return *this;
5776     }
5777     int hanging_indent() const noexcept {
5778         return hangingIndent_;
5779     }
5780 
5781     /** @brief amount of blank lines between paragraphs */
5782     formatting_ostream& paragraph_spacing(int lines) {
5783         paragraphSpacing_ = lines;
5784         return *this;
5785     }
5786     int paragraph_spacing() const noexcept {
5787         return paragraphSpacing_;
5788     }
5789 
5790     /** @brief insert paragraph spacing
5791                if paragraph is at least 'lines' lines long */
5792     formatting_ostream& min_paragraph_lines_for_spacing(int lines) {
5793         paragraphSpacingThreshold_ = lines;
5794         return *this;
5795     }
5796     int min_paragraph_lines_for_spacing() const noexcept {
5797         return paragraphSpacingThreshold_;
5798     }
5799 
5800     /** @brief if set to true, newline characters will be ignored */
5801     formatting_ostream& ignore_newline_chars(bool yes) {
5802         ignoreInputNls_ = yes;
5803         return *this;
5804     }
5805 
5806     bool ignore_newline_chars() const noexcept {
5807         return ignoreInputNls_;
5808     }
5809 
5810 
5811     //---------------------------------------------------------------
5812     /* @brief insert 'n' spaces */
5813     void write_spaces(int n) {
5814         if(n < 1) return;
5815         os_ << string_type(size_type(n), ' ');
5816         curCol_ += n;
5817     }
5818 
5819     /* @brief go to new line, but continue current paragraph */
5820     void wrap_soft(int times = 1) {
5821         if(times < 1) return;
5822         if(times > 1) {
5823             os_ << string_type(size_type(times), '\n');
5824         } else {
5825             os_ << '\n';
5826         }
5827         curCol_ = 0;
5828         ++curParagraphLines_;
5829     }
5830 
5831     /* @brief go to new line, and start a new paragraph */
5832     void wrap_hard(int times = 1) {
5833         if(times < 1) return;
5834 
5835         if(paragraph_spacing() > 0 &&
5836            paragraph_lines() >= min_paragraph_lines_for_spacing())
5837         {
5838             times = paragraph_spacing() + 1;
5839         }
5840 
5841         if(times > 1) {
5842             os_ << string_type(size_type(times), '\n');
5843             curBlankLines_ += times - 1;
5844         } else {
5845             os_ << '\n';
5846         }
5847         if(at_begin_of_line()) {
5848             ++curBlankLines_;
5849         }
5850         curCol_ = 0;
5851         curParagraphLines_ = 1;
5852     }
5853 
5854 
5855     //---------------------------------------------------------------
5856     bool at_begin_of_line() const noexcept {
5857         return curCol_ <= current_line_begin();
5858     }
5859     int current_line_begin() const noexcept {
5860         return in_hanging_part_of_paragraph()
5861             ? firstCol_ + hangingIndent_
5862             : firstCol_;
5863     }
5864 
5865     int current_column() const noexcept {
5866         return curCol_;
5867     }
5868 
5869     int total_non_blank_lines() const noexcept {
5870         return totalNonBlankLines_;
5871     }
5872     int paragraph_lines() const noexcept {
5873         return curParagraphLines_;
5874     }
5875     int blank_lines_before_paragraph() const noexcept {
5876         return curBlankLines_;
5877     }
5878 
5879 
5880     //---------------------------------------------------------------
5881     template<class T>
5882     friend formatting_ostream&
5883     operator << (formatting_ostream& os, const T& x) {
5884         os.write(x);
5885         return os;
5886     }
5887 
5888     void flush() {
5889         os_.flush();
5890     }
5891 
5892 
5893 private:
5894     bool in_hanging_part_of_paragraph() const noexcept {
5895         return hanging_indent() > 0 && paragraph_lines() > 1;
5896     }
5897     bool current_line_empty() const noexcept {
5898         return curCol_ < 1;
5899     }
5900     bool left_of_text_area() const noexcept {
5901         return curCol_ < current_line_begin();
5902     }
5903     bool right_of_text_area() const noexcept {
5904         return curCol_ > lastCol_;
5905     }
5906     int columns_left_in_line() const noexcept {
5907         return lastCol_ - std::max(current_line_begin(), curCol_);
5908     }
5909 
5910     void fix_indent() {
5911         if(left_of_text_area()) {
5912             const auto fst = current_line_begin();
5913             write_spaces(fst - curCol_);
5914             curCol_ = fst;
5915         }
5916     }
5917 
5918     template<class Iter>
5919     bool only_whitespace(Iter first, Iter last) const {
5920         return last == std::find_if_not(first, last,
5921                 [](char_type c) { return std::isspace(c); });
5922     }
5923 
5924     /** @brief write any object */
5925     template<class T>
5926     void write(const T& x) {
5927         std::ostringstream ss;
5928         ss << x;
5929         write(std::move(ss).str());
5930     }
5931 
5932     /** @brief write a stringstream */
5933     void write(const std::ostringstream& s) {
5934         write(s.str());
5935     }
5936 
5937     /** @brief write a string */
5938     void write(const string_type& s) {
5939         write(s.begin(), s.end());
5940     }
5941 
5942     /** @brief partition output into lines */
5943     template<class Iter>
5944     void write(Iter first, Iter last)
5945     {
5946         if(first == last) return;
5947         if(*first == '\n') {
5948             if(!ignore_newline_chars()) wrap_hard();
5949             ++first;
5950             if(first == last) return;
5951         }
5952         auto i = std::find(first, last, '\n');
5953         if(i != last) {
5954             if(ignore_newline_chars()) ++i;
5955             if(i != last) {
5956                 write_line(first, i);
5957                 write(i, last);
5958             }
5959         }
5960         else {
5961             write_line(first, last);
5962         }
5963     }
5964 
5965     /** @brief handle line wrapping due to column constraints */
5966     template<class Iter>
5967     void write_line(Iter first, Iter last)
5968     {
5969         if(first == last) return;
5970         if(only_whitespace(first, last)) return;
5971 
5972         if(right_of_text_area()) wrap_soft();
5973 
5974         if(at_begin_of_line()) {
5975             //discard whitespace, it we start a new line
5976             first = std::find_if(first, last,
5977                         [](char_type c) { return !std::isspace(c); });
5978             if(first == last) return;
5979         }
5980 
5981         const auto n = int(std::distance(first,last));
5982         const auto m = columns_left_in_line();
5983         //if text to be printed is too long for one line -> wrap
5984         if(n > m) {
5985             //break before word, if break is mid-word
5986             auto breakat = first + m;
5987             while(breakat > first && !std::isspace(*breakat)) --breakat;
5988             //could not find whitespace before word -> try after the word
5989             if(!std::isspace(*breakat) && breakat == first) {
5990                 breakat = std::find_if(first+m, last,
5991                           [](char_type c) { return std::isspace(c); });
5992             }
5993             if(breakat > first) {
5994                 if(curCol_ < 1) ++totalNonBlankLines_;
5995                 fix_indent();
5996                 std::copy(first, breakat, std::ostream_iterator<char_type>(os_));
5997                 curBlankLines_ = 0;
5998             }
5999             if(breakat < last) {
6000                 wrap_soft();
6001                 write_line(breakat, last);
6002             }
6003         }
6004         else {
6005             if(curCol_ < 1) ++totalNonBlankLines_;
6006             fix_indent();
6007             std::copy(first, last, std::ostream_iterator<char_type>(os_));
6008             curCol_ += n;
6009             curBlankLines_ = 0;
6010         }
6011     }
6012 
6013     /** @brief write a single character */
6014     void write(char_type c)
6015     {
6016         if(c == '\n') {
6017             if(!ignore_newline_chars()) wrap_hard();
6018         }
6019         else {
6020             if(at_begin_of_line()) ++totalNonBlankLines_;
6021             fix_indent();
6022             os_ << c;
6023             ++curCol_;
6024         }
6025     }
6026 
6027     OStream& os_;
6028     int curCol_;
6029     int firstCol_;
6030     int lastCol_;
6031     int hangingIndent_;
6032     int paragraphSpacing_;
6033     int paragraphSpacingThreshold_;
6034     int curBlankLines_;
6035     int curParagraphLines_;
6036     int totalNonBlankLines_;
6037     bool ignoreInputNls_;
6038 };
6039 
6040 
6041 }
6042 
6043 
6044 
6045 
6046 /*************************************************************************//**
6047  *
6048  * @brief   generates usage lines
6049  *
6050  * @details lazily evaluated
6051  *
6052  *****************************************************************************/
6053 class usage_lines
6054 {
6055 public:
6056     using string = doc_string;
6057 
6058     usage_lines(const group& cli, string prefix = "",
6059                 const doc_formatting& fmt = doc_formatting{})
6060     :
6061         cli_(cli), fmt_(fmt), prefix_(std::move(prefix))
6062     {
6063         if(!prefix_.empty()) prefix_ += ' ';
6064     }
6065 
6066     usage_lines(const group& cli, const doc_formatting& fmt):
6067         usage_lines(cli, "", fmt)
6068     {}
6069 
6070     usage_lines& ommit_outermost_group_surrounders(bool yes) {
6071         ommitOutermostSurrounders_ = yes;
6072         return *this;
6073     }
6074     bool ommit_outermost_group_surrounders() const {
6075         return ommitOutermostSurrounders_;
6076     }
6077 
6078     template<class OStream>
6079     inline friend OStream& operator << (OStream& os, const usage_lines& p) {
6080         p.write(os);
6081         return os;
6082     }
6083 
6084     string str() const {
6085         std::ostringstream os; os << *this; return os.str();
6086     }
6087 
6088 
6089 private:
6090     using stream_t = detail::formatting_ostream<>;
6091     const group& cli_;
6092     doc_formatting fmt_;
6093     string prefix_;
6094     bool ommitOutermostSurrounders_ = false;
6095 
6096 
6097     //-----------------------------------------------------
6098     struct context {
6099         group::depth_first_traverser pos;
6100         std::stack<string> separators;
6101         std::stack<string> postfixes;
6102         int level = 0;
6103         const group* outermost = nullptr;
6104         bool linestart = false;
6105         bool useOutermost = true;
6106         int line = 0;
6107 
6108         bool is_singleton() const noexcept {
6109             return linestart && pos.is_last_in_path();
6110         }
6111         bool is_alternative() const noexcept {
6112             return pos.parent().exclusive();
6113         }
6114     };
6115 
6116 
6117     /***************************************************************//**
6118      *
6119      * @brief writes usage text for command line parameters
6120      *
6121      *******************************************************************/
6122     template<class OStream>
6123     void write(OStream& os) const
6124     {
6125         detail::formatting_ostream<OStream> fos(os);
6126         fos.first_column(fmt_.first_column());
6127         fos.last_column(fmt_.last_column());
6128 
6129         auto hindent = int(prefix_.size());
6130         if(fos.first_column() + hindent >= int(0.4 * fos.text_width())) {
6131             hindent = fmt_.indent_size();
6132         }
6133         fos.hanging_indent(hindent);
6134 
6135         fos.paragraph_spacing(fmt_.paragraph_spacing());
6136         fos.min_paragraph_lines_for_spacing(2);
6137         fos.ignore_newline_chars(fmt_.ignore_newline_chars());
6138 
6139         context cur;
6140         cur.pos = cli_.begin_dfs();
6141         cur.linestart = true;
6142         cur.level = cur.pos.level();
6143         cur.outermost = &cli_;
6144 
6145         write(fos, cur, prefix_);
6146     }
6147 
6148 
6149     /***************************************************************//**
6150      *
6151      * @brief writes usage text for command line parameters
6152      *
6153      * @param prefix   all that goes in front of current things to print
6154      *
6155      *******************************************************************/
6156     template<class OStream>
6157     void write(OStream& os, context cur, string prefix) const
6158     {
6159         if(!cur.pos) return;
6160 
6161         std::ostringstream buf;
6162         if(cur.linestart) buf << prefix;
6163         const auto initPos = buf.tellp();
6164 
6165         cur.level = cur.pos.level();
6166 
6167         if(cur.useOutermost) {
6168             //we cannot start outside of the outermost group
6169             //so we have to treat it separately
6170             start_group(buf, cur.pos.parent(), cur);
6171             if(!cur.pos) {
6172                 os << buf.str();
6173                 return;
6174             }
6175         }
6176         else {
6177             //don't visit siblings of starter node
6178             cur.pos.skip_siblings();
6179         }
6180         check_end_group(buf, cur);
6181 
6182         do {
6183             if(buf.tellp() > initPos) cur.linestart = false;
6184             if(!cur.linestart && !cur.pos.is_first_in_parent()) {
6185                 buf << cur.separators.top();
6186             }
6187             if(cur.pos->is_group()) {
6188                 start_group(buf, cur.pos->as_group(), cur);
6189                 if(!cur.pos) {
6190                     os << buf.str();
6191                     return;
6192                 }
6193             }
6194             else {
6195                 buf << param_label(cur.pos->as_param(), cur);
6196                 ++cur.pos;
6197             }
6198             check_end_group(buf, cur);
6199         } while(cur.pos);
6200 
6201         os << buf.str();
6202     }
6203 
6204 
6205     /***************************************************************//**
6206      *
6207      * @brief handles pattern group surrounders and separators
6208      *        and alternative splitting
6209      *
6210      *******************************************************************/
6211     void start_group(std::ostringstream& os,
6212                      const group& group, context& cur) const
6213     {
6214         //does cur.pos already point to a member or to group itself?
6215         //needed for special treatment of outermost group
6216         const bool alreadyInside = &(cur.pos.parent()) == &group;
6217 
6218         auto lbl = joined_label(group, cur);
6219         if(!lbl.empty()) {
6220             os << lbl;
6221             cur.linestart = false;
6222             //skip over entire group as its label has already been created
6223             if(alreadyInside) {
6224                 cur.pos.next_after_siblings();
6225             } else {
6226                 cur.pos.next_sibling();
6227             }
6228         }
6229         else {
6230             const bool splitAlternatives = group.exclusive() &&
6231                 fmt_.split_alternatives() &&
6232                 std::any_of(group.begin(), group.end(),
6233                     [this](const pattern& p) {
6234                         return int(p.param_count()) >= fmt_.alternatives_min_split_size();
6235                     });
6236 
6237             if(splitAlternatives) {
6238                 cur.postfixes.push("");
6239                 cur.separators.push("");
6240                 //recursively print alternative paths in decision-DAG
6241                 //enter group?
6242                 if(!alreadyInside) ++cur.pos;
6243                 cur.linestart = true;
6244                 cur.useOutermost = false;
6245                 auto pfx = os.str();
6246                 os.str("");
6247                 //print paths in DAG starting at each group member
6248                 for(std::size_t i = 0; i < group.size(); ++i) {
6249                     std::stringstream buf;
6250                     cur.outermost = cur.pos->is_group() ? &(cur.pos->as_group()) : nullptr;
6251                     write(buf, cur, pfx);
6252                     if(buf.tellp() > int(pfx.size())) {
6253                         os << buf.str();
6254                         if(i < group.size()-1) {
6255                             if(cur.line > 0) {
6256                                 os << string(fmt_.line_spacing(), '\n');
6257                             }
6258                             ++cur.line;
6259                             os << '\n';
6260                         }
6261                     }
6262                     cur.pos.next_sibling(); //do not descend into members
6263                 }
6264                 cur.pos.invalidate(); //signal end-of-path
6265                 return;
6266             }
6267             else {
6268                 //pre & postfixes, separators
6269                 auto surround = group_surrounders(group, cur);
6270                 os << surround.first;
6271                 cur.postfixes.push(std::move(surround.second));
6272                 cur.separators.push(group_separator(group, fmt_));
6273                 //descend into group?
6274                 if(!alreadyInside) ++cur.pos;
6275             }
6276         }
6277         cur.level = cur.pos.level();
6278     }
6279 
6280 
6281     /***************************************************************//**
6282      *
6283      *******************************************************************/
6284     void check_end_group(std::ostringstream& os, context& cur) const
6285     {
6286         for(; cur.level > cur.pos.level(); --cur.level) {
6287             os << cur.postfixes.top();
6288             cur.postfixes.pop();
6289             cur.separators.pop();
6290         }
6291         cur.level = cur.pos.level();
6292     }
6293 
6294 
6295     /***************************************************************//**
6296      *
6297      * @brief makes usage label for one command line parameter
6298      *
6299      *******************************************************************/
6300     string param_label(const parameter& p, const context& cur) const
6301     {
6302         const auto& parent = cur.pos.parent();
6303 
6304         const bool startsOptionalSequence =
6305             parent.size() > 1 && p.blocking() && cur.pos.is_first_in_parent();
6306 
6307         const bool outermost =
6308             ommitOutermostSurrounders_ && cur.outermost == &parent;
6309 
6310         const bool showopt = !cur.is_alternative() && !p.required()
6311             && !startsOptionalSequence && !outermost;
6312 
6313         const bool showrep = p.repeatable() && !outermost;
6314 
6315         string lbl;
6316 
6317         if(showrep) lbl += fmt_.repeat_prefix();
6318         if(showopt) lbl += fmt_.optional_prefix();
6319 
6320         const auto& flags = p.flags();
6321         if(!flags.empty()) {
6322             const int n = std::min(fmt_.max_flags_per_param_in_usage(),
6323                                    int(flags.size()));
6324 
6325             const bool surrAlt = n > 1 && !showopt && !cur.is_singleton();
6326 
6327             if(surrAlt) lbl += fmt_.alternative_flags_prefix();
6328             bool sep = false;
6329             for(int i = 0; i < n; ++i) {
6330                 if(sep) {
6331                     if(cur.is_singleton())
6332                         lbl += fmt_.alternative_group_separator();
6333                     else
6334                         lbl += fmt_.flag_separator();
6335                 }
6336                 lbl += flags[i];
6337                 sep = true;
6338             }
6339             if(surrAlt) lbl += fmt_.alternative_flags_postfix();
6340         }
6341         else {
6342              if(!p.label().empty()) {
6343                  lbl += fmt_.label_prefix()
6344                      + p.label()
6345                      + fmt_.label_postfix();
6346              } else if(!fmt_.empty_label().empty()) {
6347                  lbl += fmt_.label_prefix()
6348                      + fmt_.empty_label()
6349                      + fmt_.label_postfix();
6350              } else {
6351                  return "";
6352              }
6353         }
6354 
6355         if(showopt) lbl += fmt_.optional_postfix();
6356         if(showrep) lbl += fmt_.repeat_postfix();
6357 
6358         return lbl;
6359     }
6360 
6361 
6362     /***************************************************************//**
6363      *
6364      * @brief prints flags in one group in a merged fashion
6365      *
6366      *******************************************************************/
6367     string joined_label(const group& g, const context& cur) const
6368     {
6369         if(!fmt_.merge_alternative_flags_with_common_prefix() &&
6370            !fmt_.merge_joinable_with_common_prefix()) return "";
6371 
6372         const bool flagsonly = std::all_of(g.begin(), g.end(),
6373             [](const pattern& p){
6374                 return p.is_param() && !p.as_param().flags().empty();
6375             });
6376 
6377         if(!flagsonly) return "";
6378 
6379         const bool showOpt = g.all_optional() &&
6380             !(ommitOutermostSurrounders_ && cur.outermost == &g);
6381 
6382         auto pfx = g.common_flag_prefix();
6383         if(pfx.empty()) return "";
6384 
6385         const auto n = pfx.size();
6386         if(g.exclusive() &&
6387            fmt_.merge_alternative_flags_with_common_prefix())
6388         {
6389             string lbl;
6390             if(showOpt) lbl += fmt_.optional_prefix();
6391             lbl += pfx + fmt_.alternatives_prefix();
6392             bool first = true;
6393             for(const auto& p : g) {
6394                 if(p.is_param()) {
6395                     if(first)
6396                         first = false;
6397                     else
6398                         lbl += fmt_.alternative_param_separator();
6399                     lbl += p.as_param().flags().front().substr(n);
6400                 }
6401             }
6402             lbl += fmt_.alternatives_postfix();
6403             if(showOpt) lbl += fmt_.optional_postfix();
6404             return lbl;
6405         }
6406         //no alternatives, but joinable flags
6407         else if(g.joinable() &&
6408             fmt_.merge_joinable_with_common_prefix())
6409         {
6410             const bool allSingleChar = std::all_of(g.begin(), g.end(),
6411                 [&](const pattern& p){
6412                     return p.is_param() &&
6413                         p.as_param().flags().front().substr(n).size() == 1;
6414                 });
6415 
6416             if(allSingleChar) {
6417                 string lbl;
6418                 if(showOpt) lbl += fmt_.optional_prefix();
6419                 lbl += pfx;
6420                 for(const auto& p : g) {
6421                     if(p.is_param())
6422                         lbl += p.as_param().flags().front().substr(n);
6423                 }
6424                 if(showOpt) lbl += fmt_.optional_postfix();
6425                 return lbl;
6426             }
6427         }
6428 
6429         return "";
6430     }
6431 
6432 
6433     /***************************************************************//**
6434      *
6435      * @return symbols with which to surround a group
6436      *
6437      *******************************************************************/
6438     std::pair<string,string>
6439     group_surrounders(const group& group, const context& cur) const
6440     {
6441         string prefix;
6442         string postfix;
6443 
6444         const bool isOutermost = &group == cur.outermost;
6445         if(isOutermost && ommitOutermostSurrounders_)
6446             return {string{}, string{}};
6447 
6448         if(group.exclusive()) {
6449             if(group.all_optional()) {
6450                 prefix  = fmt_.optional_prefix();
6451                 postfix = fmt_.optional_postfix();
6452                 if(group.all_flagless()) {
6453                     prefix  += fmt_.label_prefix();
6454                     postfix = fmt_.label_prefix() + postfix;
6455                 }
6456             } else if(group.all_flagless()) {
6457                 prefix  = fmt_.label_prefix();
6458                 postfix = fmt_.label_postfix();
6459             } else if(!cur.is_singleton() || !isOutermost) {
6460                 prefix  = fmt_.alternatives_prefix();
6461                 postfix = fmt_.alternatives_postfix();
6462             }
6463         }
6464         else if(group.size() > 1 &&
6465                 group.front().blocking() && !group.front().required())
6466         {
6467             prefix  = fmt_.optional_prefix();
6468             postfix = fmt_.optional_postfix();
6469         }
6470         else if(group.size() > 1 && cur.is_alternative() &&
6471                 &group != cur.outermost)
6472         {
6473             prefix  = fmt_.group_prefix();
6474             postfix = fmt_.group_postfix();
6475         }
6476         else if(!group.exclusive() &&
6477             group.joinable() && !cur.linestart)
6478         {
6479             prefix  = fmt_.joinable_prefix();
6480             postfix = fmt_.joinable_postfix();
6481         }
6482 
6483         if(group.repeatable()) {
6484             if(prefix.empty()) prefix = fmt_.group_prefix();
6485             prefix = fmt_.repeat_prefix() + prefix;
6486             if(postfix.empty()) postfix = fmt_.group_postfix();
6487             postfix += fmt_.repeat_postfix();
6488         }
6489 
6490         return {std::move(prefix), std::move(postfix)};
6491     }
6492 
6493 
6494     /***************************************************************//**
6495      *
6496      * @return symbol that separates members of a group
6497      *
6498      *******************************************************************/
6499     static string
6500     group_separator(const group& group, const doc_formatting& fmt)
6501     {
6502         const bool only1ParamPerMember = std::all_of(group.begin(), group.end(),
6503             [](const pattern& p) { return p.param_count() < 2; });
6504 
6505         if(only1ParamPerMember) {
6506             if(group.exclusive()) {
6507                 return fmt.alternative_param_separator();
6508             } else {
6509                 return fmt.param_separator();
6510             }
6511         }
6512         else { //there is at least one large group inside
6513             if(group.exclusive()) {
6514                 return fmt.alternative_group_separator();
6515             } else {
6516                 return fmt.group_separator();
6517             }
6518         }
6519     }
6520 };
6521 
6522 
6523 
6524 
6525 /*************************************************************************//**
6526  *
6527  * @brief   generates parameter and group documentation from docstrings
6528  *
6529  * @details lazily evaluated
6530  *
6531  *****************************************************************************/
6532 class documentation
6533 {
6534 public:
6535     using string          = doc_string;
6536     using filter_function = std::function<bool(const parameter&)>;
6537 
6538     documentation(const group& cli,
6539                   const doc_formatting& fmt = doc_formatting{},
6540                   filter_function filter = param_filter{})
6541     :
6542         cli_(cli), fmt_{fmt}, usgFmt_{fmt}, filter_{std::move(filter)}
6543     {
6544         //necessary, because we re-use "usage_lines" to generate
6545         //labels for documented groups
6546         usgFmt_.max_flags_per_param_in_usage(
6547             usgFmt_.max_flags_per_param_in_doc());
6548     }
6549 
6550     documentation(const group& cli, filter_function filter) :
6551         documentation{cli, doc_formatting{}, std::move(filter)}
6552     {}
6553 
6554     documentation(const group& cli, const param_filter& filter) :
6555         documentation{cli, doc_formatting{},
6556                       [filter](const parameter& p) { return filter(p); }}
6557     {}
6558 
6559     template<class OStream>
6560     inline friend OStream& operator << (OStream& os, const documentation& p) {
6561         p.write(os);
6562         return os;
6563     }
6564 
6565     string str() const {
6566         std::ostringstream os;
6567         write(os);
6568         return os.str();
6569     }
6570 
6571 
6572 private:
6573     using dfs_traverser = group::depth_first_traverser;
6574 
6575     const group& cli_;
6576     doc_formatting fmt_;
6577     doc_formatting usgFmt_;
6578     filter_function filter_;
6579     enum class paragraph { param, group };
6580 
6581 
6582     /***************************************************************//**
6583      *
6584      * @brief writes documentation to output stream
6585      *
6586      *******************************************************************/
6587      template<class OStream>
6588      void write(OStream& os) const {
6589         detail::formatting_ostream<OStream> fos(os);
6590         fos.first_column(fmt_.first_column());
6591         fos.last_column(fmt_.last_column());
6592         fos.hanging_indent(0);
6593         fos.paragraph_spacing(0);
6594         fos.ignore_newline_chars(fmt_.ignore_newline_chars());
6595         print_doc(fos, cli_);
6596      }
6597 
6598 
6599     /***************************************************************//**
6600      *
6601      * @brief writes full documentation text for command line parameters
6602      *
6603      *******************************************************************/
6604     template<class OStream>
6605     void print_doc(detail::formatting_ostream<OStream>& os,
6606                    const group& cli, int indentLvl = 0) const
6607     {
6608         if(cli.empty()) return;
6609 
6610         //if group itself doesn't have docstring
6611         if(cli.doc().empty()) {
6612             for(const auto& p : cli) {
6613                 print_doc(os, p, indentLvl);
6614             }
6615         }
6616         else { //group itself does have docstring
6617             bool anyDocInside = std::any_of(cli.begin(), cli.end(),
6618                 [](const pattern& p){ return !p.doc().empty(); });
6619 
6620             if(anyDocInside) { //group docstring as title, then child entries
6621                 handle_spacing(os, paragraph::group, indentLvl);
6622                 os << cli.doc();
6623                 for(const auto& p : cli) {
6624                     print_doc(os, p, indentLvl + 1);
6625                 }
6626             }
6627             else { //group label first then group docstring
6628                 auto lbl = usage_lines(cli, usgFmt_)
6629                            .ommit_outermost_group_surrounders(true).str();
6630 
6631                 str::trim(lbl);
6632                 handle_spacing(os, paragraph::param, indentLvl);
6633                 print_entry(os, lbl, cli.doc());
6634             }
6635         }
6636     }
6637 
6638 
6639     /***************************************************************//**
6640      *
6641      * @brief writes documentation text for one group or parameter
6642      *
6643      *******************************************************************/
6644     template<class OStream>
6645     void print_doc(detail::formatting_ostream<OStream>& os,
6646                    const pattern& ptrn, int indentLvl) const
6647     {
6648         if(ptrn.is_group()) {
6649             print_doc(os, ptrn.as_group(), indentLvl);
6650         }
6651         else {
6652             const auto& p = ptrn.as_param();
6653             if(!filter_(p)) return;
6654 
6655             handle_spacing(os, paragraph::param, indentLvl);
6656             print_entry(os, param_label(p, fmt_), p.doc());
6657         }
6658     }
6659 
6660     /***************************************************************//**
6661      *
6662      * @brief handles line and paragraph spacings
6663      *
6664      *******************************************************************/
6665     template<class OStream>
6666     void handle_spacing(detail::formatting_ostream<OStream>& os,
6667                         paragraph p, int indentLvl) const
6668     {
6669         const auto oldIndent = os.first_column();
6670         const auto indent = fmt_.first_column() + indentLvl * fmt_.indent_size();
6671 
6672         if(os.total_non_blank_lines() < 1) {
6673             os.first_column(indent);
6674             return;
6675         }
6676 
6677         if(os.paragraph_lines() > 1 || indent < oldIndent) {
6678             os.wrap_hard(fmt_.paragraph_spacing() + 1);
6679         } else {
6680             os.wrap_hard();
6681         }
6682 
6683         if(p == paragraph::group) {
6684             if(os.blank_lines_before_paragraph() < fmt_.paragraph_spacing()) {
6685                 os.wrap_hard(fmt_.paragraph_spacing() - os.blank_lines_before_paragraph());
6686             }
6687         }
6688         else if(os.blank_lines_before_paragraph() < fmt_.line_spacing()) {
6689             os.wrap_hard(fmt_.line_spacing() - os.blank_lines_before_paragraph());
6690         }
6691         os.first_column(indent);
6692     }
6693 
6694     /*********************************************************************//**
6695      *
6696      * @brief prints one entry = label + docstring
6697      *
6698      ************************************************************************/
6699     template<class OStream>
6700     void print_entry(detail::formatting_ostream<OStream>& os,
6701                      const string& label, const string& docstr) const
6702     {
6703         if(label.empty()) return;
6704 
6705         os << label;
6706 
6707         if(!docstr.empty()) {
6708             if(os.current_column() >= fmt_.doc_column()) os.wrap_soft();
6709             const auto oldcol = os.first_column();
6710             os.first_column(fmt_.doc_column());
6711             os << docstr;
6712             os.first_column(oldcol);
6713         }
6714     }
6715 
6716 
6717     /*********************************************************************//**
6718      *
6719      * @brief makes label for one parameter
6720      *
6721      ************************************************************************/
6722     static doc_string
6723     param_label(const parameter& param, const doc_formatting& fmt)
6724     {
6725         doc_string lbl;
6726 
6727         if(param.repeatable()) lbl += fmt.repeat_prefix();
6728 
6729         const auto& flags = param.flags();
6730         if(!flags.empty()) {
6731             lbl += flags[0];
6732             const int n = std::min(fmt.max_flags_per_param_in_doc(),
6733                                    int(flags.size()));
6734             for(int i = 1; i < n; ++i) {
6735                 lbl += fmt.flag_separator() + flags[i];
6736             }
6737         }
6738         else if(!param.label().empty() || !fmt.empty_label().empty()) {
6739             lbl += fmt.label_prefix();
6740             if(!param.label().empty()) {
6741                 lbl += param.label();
6742             } else {
6743                 lbl += fmt.empty_label();
6744             }
6745             lbl += fmt.label_postfix();
6746         }
6747 
6748         if(param.repeatable()) lbl += fmt.repeat_postfix();
6749 
6750         return lbl;
6751     }
6752 
6753 };
6754 
6755 
6756 
6757 
6758 /*************************************************************************//**
6759  *
6760  * @brief stores strings for man page sections
6761  *
6762  *****************************************************************************/
6763 class man_page
6764 {
6765 public:
6766     //---------------------------------------------------------------
6767     using string = doc_string;
6768 
6769     //---------------------------------------------------------------
6770     /** @brief man page section */
6771     class section {
6772     public:
6773         using string = doc_string;
6774 
6775         section(string stitle, string scontent):
6776             title_{std::move(stitle)}, content_{std::move(scontent)}
6777         {}
6778 
6779         const string& title()   const noexcept { return title_; }
6780         const string& content() const noexcept { return content_; }
6781 
6782     private:
6783         string title_;
6784         string content_;
6785     };
6786 
6787 private:
6788     using section_store = std::vector<section>;
6789 
6790 public:
6791     //---------------------------------------------------------------
6792     using value_type     = section;
6793     using const_iterator = section_store::const_iterator;
6794     using size_type      = section_store::size_type;
6795 
6796 
6797     //---------------------------------------------------------------
6798     man_page&
6799     append_section(string title, string content)
6800     {
6801         sections_.emplace_back(std::move(title), std::move(content));
6802         return *this;
6803     }
6804     //-----------------------------------------------------
6805     man_page&
6806     prepend_section(string title, string content)
6807     {
6808         sections_.emplace(sections_.begin(),
6809                           std::move(title), std::move(content));
6810         return *this;
6811     }
6812 
6813 
6814     //---------------------------------------------------------------
6815     const section& operator [] (size_type index) const noexcept {
6816         return sections_[index];
6817     }
6818 
6819     //---------------------------------------------------------------
6820     size_type size() const noexcept { return sections_.size(); }
6821 
6822     bool empty() const noexcept { return sections_.empty(); }
6823 
6824 
6825     //---------------------------------------------------------------
6826     const_iterator begin() const noexcept { return sections_.begin(); }
6827     const_iterator end()   const noexcept { return sections_.end(); }
6828 
6829 
6830     //---------------------------------------------------------------
6831     man_page& program_name(const string& n) {
6832         progName_ = n;
6833         return *this;
6834     }
6835     man_page& program_name(string&& n) {
6836         progName_ = std::move(n);
6837         return *this;
6838     }
6839     const string& program_name() const noexcept {
6840         return progName_;
6841     }
6842 
6843 
6844     //---------------------------------------------------------------
6845     man_page& section_row_spacing(int rows) {
6846         sectionSpc_ = rows > 0 ? rows : 0;
6847         return *this;
6848     }
6849     int section_row_spacing() const noexcept { return sectionSpc_; }
6850 
6851 
6852 private:
6853     int sectionSpc_ = 1;
6854     section_store sections_;
6855     string progName_;
6856 };
6857 
6858 
6859 
6860 /*************************************************************************//**
6861  *
6862  * @brief generates man sections from command line parameters
6863  *        with sections "synopsis" and "options"
6864  *
6865  *****************************************************************************/
6866 inline man_page
6867 make_man_page(const group& cli,
6868               doc_string progname = "",
6869               const doc_formatting& fmt = doc_formatting{})
6870 {
6871     man_page man;
6872     man.append_section("SYNOPSIS", usage_lines(cli,progname,fmt).str());
6873     man.append_section("OPTIONS", documentation(cli,fmt).str());
6874     return man;
6875 }
6876 
6877 
6878 
6879 /*************************************************************************//**
6880  *
6881  * @brief   generates man page based on command line parameters
6882  *
6883  *****************************************************************************/
6884 template<class OStream>
6885 OStream&
6886 operator << (OStream& os, const man_page& man)
6887 {
6888     bool first = true;
6889     const auto secSpc = doc_string(man.section_row_spacing() + 1, '\n');
6890     for(const auto& section : man) {
6891         if(!section.content().empty()) {
6892             if(first) first = false; else os << secSpc;
6893             if(!section.title().empty()) os << section.title() << '\n';
6894             os << section.content();
6895         }
6896     }
6897     os << '\n';
6898     return os;
6899 }
6900 
6901 
6902 
6903 
6904 
6905 /*************************************************************************//**
6906  *
6907  * @brief printing methods for debugging command line interfaces
6908  *
6909  *****************************************************************************/
6910 namespace debug {
6911 
6912 
6913 /*************************************************************************//**
6914  *
6915  * @brief prints first flag or value label of a parameter
6916  *
6917  *****************************************************************************/
6918 inline doc_string doc_label(const parameter& p)
6919 {
6920     if(!p.flags().empty()) return p.flags().front();
6921     if(!p.label().empty()) return p.label();
6922     return doc_string{"<?>"};
6923 }
6924 
6925 inline doc_string doc_label(const group&)
6926 {
6927     return "<group>";
6928 }
6929 
6930 inline doc_string doc_label(const pattern& p)
6931 {
6932     return p.is_group() ? doc_label(p.as_group()) : doc_label(p.as_param());
6933 }
6934 
6935 
6936 /*************************************************************************//**
6937  *
6938  * @brief prints parsing result
6939  *
6940  *****************************************************************************/
6941 template<class OStream>
6942 void print(OStream& os, const parsing_result& result)
6943 {
6944     for(const auto& m : result) {
6945         os << "#" << m.index() << " " << m.arg() << " -> ";
6946         auto p = m.param();
6947         if(p) {
6948             os << doc_label(*p) << " \t";
6949             if(m.repeat() > 0) {
6950                 os << (m.bad_repeat() ? "[bad repeat " : "[repeat ")
6951                    <<  m.repeat() << "]";
6952             }
6953             if(m.blocked())  os << " [blocked]";
6954             if(m.conflict()) os << " [conflict]";
6955             os << '\n';
6956         }
6957         else {
6958             os << " [unmapped]\n";
6959         }
6960     }
6961 
6962     for(const auto& m : result.missing()) {
6963         auto p = m.param();
6964         if(p) {
6965             os << doc_label(*p) << " \t";
6966             os << " [missing after " << m.after_index() << "]\n";
6967         }
6968     }
6969 }
6970 
6971 
6972 /*************************************************************************//**
6973  *
6974  * @brief prints parameter label and some properties
6975  *
6976  *****************************************************************************/
6977 template<class OStream>
6978 void print(OStream& os, const parameter& p)
6979 {
6980     if(p.greedy()) os << '!';
6981     if(p.blocking()) os << '~';
6982     if(!p.required()) os << '[';
6983     os << doc_label(p);
6984     if(p.repeatable()) os << "...";
6985     if(!p.required()) os << "]";
6986 }
6987 
6988 
6989 //-------------------------------------------------------------------
6990 template<class OStream>
6991 void print(OStream& os, const group& g, int level = 0);
6992 
6993 
6994 /*************************************************************************//**
6995  *
6996  * @brief prints group or parameter; uses indentation
6997  *
6998  *****************************************************************************/
6999 template<class OStream>
7000 void print(OStream& os, const pattern& param, int level = 0)
7001 {
7002     if(param.is_group()) {
7003         print(os, param.as_group(), level);
7004     }
7005     else {
7006         os << doc_string(4*level, ' ');
7007         print(os, param.as_param());
7008     }
7009 }
7010 
7011 
7012 /*************************************************************************//**
7013  *
7014  * @brief prints group and its contents; uses indentation
7015  *
7016  *****************************************************************************/
7017 template<class OStream>
7018 void print(OStream& os, const group& g, int level)
7019 {
7020     auto indent = doc_string(4*level, ' ');
7021     os << indent;
7022     if(g.blocking()) os << '~';
7023     if(g.joinable()) os << 'J';
7024     os << (g.exclusive() ? "(|\n" : "(\n");
7025     for(const auto& p : g) {
7026         print(os, p, level+1);
7027     }
7028     os << '\n' << indent << (g.exclusive() ? "|)" : ")");
7029     if(g.repeatable()) os << "...";
7030     os << '\n';
7031 }
7032 
7033 
7034 } // namespace debug
7035 } //namespace clipp
7036 
7037 #endif
7038