• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 Two Blue Cubes Ltd. All rights reserved.
2 //
3 // Distributed under the Boost Software License, Version 1.0. (See accompanying
4 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5 //
6 // See https://github.com/philsquared/Clara for more details
7 
8 // Clara v1.1.5
9 
10 #ifndef CLARA_HPP_INCLUDED
11 #define CLARA_HPP_INCLUDED
12 
13 #ifndef CLARA_CONFIG_CONSOLE_WIDTH
14 #define CLARA_CONFIG_CONSOLE_WIDTH 80
15 #endif
16 
17 #ifndef CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH
18 #define CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH
19 #endif
20 
21 #ifndef CLARA_CONFIG_OPTIONAL_TYPE
22 #ifdef __has_include
23 #if __has_include(<optional>) && __cplusplus >= 201703L
24 #include <optional>
25 #define CLARA_CONFIG_OPTIONAL_TYPE std::optional
26 #endif
27 #endif
28 #endif
29 
30 
31 // ----------- #included from clara_textflow.hpp -----------
32 
33 // TextFlowCpp
34 //
35 // A single-header library for wrapping and laying out basic text, by Phil Nash
36 //
37 // Distributed under the Boost Software License, Version 1.0. (See accompanying
38 // file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
39 //
40 // This project is hosted at https://github.com/philsquared/textflowcpp
41 
42 #ifndef CLARA_TEXTFLOW_HPP_INCLUDED
43 #define CLARA_TEXTFLOW_HPP_INCLUDED
44 
45 #include <cassert>
46 #include <ostream>
47 #include <sstream>
48 #include <vector>
49 
50 #ifndef CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH
51 #define CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH 80
52 #endif
53 
54 
55 namespace clara { namespace TextFlow {
56 
isWhitespace(char c)57     inline auto isWhitespace( char c ) -> bool {
58         static std::string chars = " \t\n\r";
59         return chars.find( c ) != std::string::npos;
60     }
isBreakableBefore(char c)61     inline auto isBreakableBefore( char c ) -> bool {
62         static std::string chars = "[({<|";
63         return chars.find( c ) != std::string::npos;
64     }
isBreakableAfter(char c)65     inline auto isBreakableAfter( char c ) -> bool {
66         static std::string chars = "])}>.,:;*+-=&/\\";
67         return chars.find( c ) != std::string::npos;
68     }
69 
70     class Columns;
71 
72     class Column {
73         std::vector<std::string> m_strings;
74         size_t m_width = CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH;
75         size_t m_indent = 0;
76         size_t m_initialIndent = std::string::npos;
77 
78     public:
79         class iterator {
80             friend Column;
81 
82             Column const& m_column;
83             size_t m_stringIndex = 0;
84             size_t m_pos = 0;
85 
86             size_t m_len = 0;
87             size_t m_end = 0;
88             bool m_suffix = false;
89 
iterator(Column const & column,size_t stringIndex)90             iterator( Column const& column, size_t stringIndex )
91             :   m_column( column ),
92                 m_stringIndex( stringIndex )
93             {}
94 
line() const95             auto line() const -> std::string const& { return m_column.m_strings[m_stringIndex]; }
96 
isBoundary(size_t at) const97             auto isBoundary( size_t at ) const -> bool {
98                 assert( at > 0 );
99                 assert( at <= line().size() );
100 
101                 return at == line().size() ||
102                        ( isWhitespace( line()[at] ) && !isWhitespace( line()[at-1] ) ) ||
103                        isBreakableBefore( line()[at] ) ||
104                        isBreakableAfter( line()[at-1] );
105             }
106 
calcLength()107             void calcLength() {
108                 assert( m_stringIndex < m_column.m_strings.size() );
109 
110                 m_suffix = false;
111                 auto width = m_column.m_width-indent();
112                 m_end = m_pos;
113                 if (line()[m_pos] == '\n') {
114                     ++m_end;
115                 }
116                 while( m_end < line().size() && line()[m_end] != '\n' )
117                     ++m_end;
118 
119                 if( m_end < m_pos + width ) {
120                     m_len = m_end - m_pos;
121                 }
122                 else {
123                     size_t len = width;
124                     while (len > 0 && !isBoundary(m_pos + len))
125                         --len;
126                     while (len > 0 && isWhitespace( line()[m_pos + len - 1] ))
127                         --len;
128 
129                     if (len > 0) {
130                         m_len = len;
131                     } else {
132                         m_suffix = true;
133                         m_len = width - 1;
134                     }
135                 }
136             }
137 
indent() const138             auto indent() const -> size_t {
139                 auto initial = m_pos == 0 && m_stringIndex == 0 ? m_column.m_initialIndent : std::string::npos;
140                 return initial == std::string::npos ? m_column.m_indent : initial;
141             }
142 
addIndentAndSuffix(std::string const & plain) const143             auto addIndentAndSuffix(std::string const &plain) const -> std::string {
144                 return std::string( indent(), ' ' ) + (m_suffix ? plain + "-" : plain);
145             }
146 
147         public:
148             using difference_type = std::ptrdiff_t;
149             using value_type = std::string;
150             using pointer = value_type*;
151             using reference = value_type&;
152             using iterator_category = std::forward_iterator_tag;
153 
iterator(Column const & column)154             explicit iterator( Column const& column ) : m_column( column ) {
155                 assert( m_column.m_width > m_column.m_indent );
156                 assert( m_column.m_initialIndent == std::string::npos || m_column.m_width > m_column.m_initialIndent );
157                 calcLength();
158                 if( m_len == 0 )
159                     m_stringIndex++; // Empty string
160             }
161 
operator *() const162             auto operator *() const -> std::string {
163                 assert( m_stringIndex < m_column.m_strings.size() );
164                 assert( m_pos <= m_end );
165                 return addIndentAndSuffix(line().substr(m_pos, m_len));
166             }
167 
operator ++()168             auto operator ++() -> iterator& {
169                 m_pos += m_len;
170                 if( m_pos < line().size() && line()[m_pos] == '\n' )
171                     m_pos += 1;
172                 else
173                     while( m_pos < line().size() && isWhitespace( line()[m_pos] ) )
174                         ++m_pos;
175 
176                 if( m_pos == line().size() ) {
177                     m_pos = 0;
178                     ++m_stringIndex;
179                 }
180                 if( m_stringIndex < m_column.m_strings.size() )
181                     calcLength();
182                 return *this;
183             }
operator ++(int)184             auto operator ++(int) -> iterator {
185                 iterator prev( *this );
186                 operator++();
187                 return prev;
188             }
189 
operator ==(iterator const & other) const190             auto operator ==( iterator const& other ) const -> bool {
191                 return
192                     m_pos == other.m_pos &&
193                     m_stringIndex == other.m_stringIndex &&
194                     &m_column == &other.m_column;
195             }
operator !=(iterator const & other) const196             auto operator !=( iterator const& other ) const -> bool {
197                 return !operator==( other );
198             }
199         };
200         using const_iterator = iterator;
201 
Column(std::string const & text)202         explicit Column( std::string const& text ) { m_strings.push_back( text ); }
203 
width(size_t newWidth)204         auto width( size_t newWidth ) -> Column& {
205             assert( newWidth > 0 );
206             m_width = newWidth;
207             return *this;
208         }
indent(size_t newIndent)209         auto indent( size_t newIndent ) -> Column& {
210             m_indent = newIndent;
211             return *this;
212         }
initialIndent(size_t newIndent)213         auto initialIndent( size_t newIndent ) -> Column& {
214             m_initialIndent = newIndent;
215             return *this;
216         }
217 
width() const218         auto width() const -> size_t { return m_width; }
begin() const219         auto begin() const -> iterator { return iterator( *this ); }
end() const220         auto end() const -> iterator { return { *this, m_strings.size() }; }
221 
operator <<(std::ostream & os,Column const & col)222         inline friend std::ostream& operator << ( std::ostream& os, Column const& col ) {
223             bool first = true;
224             for( auto line : col ) {
225                 if( first )
226                     first = false;
227                 else
228                     os << "\n";
229                 os <<  line;
230             }
231             return os;
232         }
233 
234         auto operator + ( Column const& other ) -> Columns;
235 
toString() const236         auto toString() const -> std::string {
237             std::ostringstream oss;
238             oss << *this;
239             return oss.str();
240         }
241     };
242 
243     class Spacer : public Column {
244 
245     public:
Spacer(size_t spaceWidth)246         explicit Spacer( size_t spaceWidth ) : Column( "" ) {
247             width( spaceWidth );
248         }
249     };
250 
251     class Columns {
252         std::vector<Column> m_columns;
253 
254     public:
255 
256         class iterator {
257             friend Columns;
258             struct EndTag {};
259 
260             std::vector<Column> const& m_columns;
261             std::vector<Column::iterator> m_iterators;
262             size_t m_activeIterators;
263 
iterator(Columns const & columns,EndTag)264             iterator( Columns const& columns, EndTag )
265             :   m_columns( columns.m_columns ),
266                 m_activeIterators( 0 )
267             {
268                 m_iterators.reserve( m_columns.size() );
269 
270                 for( auto const& col : m_columns )
271                     m_iterators.push_back( col.end() );
272             }
273 
274         public:
275             using difference_type = std::ptrdiff_t;
276             using value_type = std::string;
277             using pointer = value_type*;
278             using reference = value_type&;
279             using iterator_category = std::forward_iterator_tag;
280 
iterator(Columns const & columns)281             explicit iterator( Columns const& columns )
282             :   m_columns( columns.m_columns ),
283                 m_activeIterators( m_columns.size() )
284             {
285                 m_iterators.reserve( m_columns.size() );
286 
287                 for( auto const& col : m_columns )
288                     m_iterators.push_back( col.begin() );
289             }
290 
operator ==(iterator const & other) const291             auto operator ==( iterator const& other ) const -> bool {
292                 return m_iterators == other.m_iterators;
293             }
operator !=(iterator const & other) const294             auto operator !=( iterator const& other ) const -> bool {
295                 return m_iterators != other.m_iterators;
296             }
operator *() const297             auto operator *() const -> std::string {
298                 std::string row, padding;
299 
300                 for( size_t i = 0; i < m_columns.size(); ++i ) {
301                     auto width = m_columns[i].width();
302                     if( m_iterators[i] != m_columns[i].end() ) {
303                         std::string col = *m_iterators[i];
304                         row += padding + col;
305                         if( col.size() < width )
306                             padding = std::string( width - col.size(), ' ' );
307                         else
308                             padding = "";
309                     }
310                     else {
311                         padding += std::string( width, ' ' );
312                     }
313                 }
314                 return row;
315             }
operator ++()316             auto operator ++() -> iterator& {
317                 for( size_t i = 0; i < m_columns.size(); ++i ) {
318                     if (m_iterators[i] != m_columns[i].end())
319                         ++m_iterators[i];
320                 }
321                 return *this;
322             }
operator ++(int)323             auto operator ++(int) -> iterator {
324                 iterator prev( *this );
325                 operator++();
326                 return prev;
327             }
328         };
329         using const_iterator = iterator;
330 
begin() const331         auto begin() const -> iterator { return iterator( *this ); }
end() const332         auto end() const -> iterator { return { *this, iterator::EndTag() }; }
333 
operator +=(Column const & col)334         auto operator += ( Column const& col ) -> Columns& {
335             m_columns.push_back( col );
336             return *this;
337         }
operator +(Column const & col)338         auto operator + ( Column const& col ) -> Columns {
339             Columns combined = *this;
340             combined += col;
341             return combined;
342         }
343 
operator <<(std::ostream & os,Columns const & cols)344         inline friend std::ostream& operator << ( std::ostream& os, Columns const& cols ) {
345 
346             bool first = true;
347             for( auto line : cols ) {
348                 if( first )
349                     first = false;
350                 else
351                     os << "\n";
352                 os << line;
353             }
354             return os;
355         }
356 
toString() const357         auto toString() const -> std::string {
358             std::ostringstream oss;
359             oss << *this;
360             return oss.str();
361         }
362     };
363 
operator +(Column const & other)364     inline auto Column::operator + ( Column const& other ) -> Columns {
365         Columns cols;
366         cols += *this;
367         cols += other;
368         return cols;
369     }
370 }}
371 
372 #endif // CLARA_TEXTFLOW_HPP_INCLUDED
373 
374 // ----------- end of #include from clara_textflow.hpp -----------
375 // ........... back in clara.hpp
376 
377 
378 #include <memory>
379 #include <set>
380 #include <algorithm>
381 
382 #if !defined(CLARA_PLATFORM_WINDOWS) && ( defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) )
383 #define CLARA_PLATFORM_WINDOWS
384 #endif
385 
386 namespace clara {
387 namespace detail {
388 
389     // Traits for extracting arg and return type of lambdas (for single argument lambdas)
390     template<typename L>
391     struct UnaryLambdaTraits : UnaryLambdaTraits<decltype( &L::operator() )> {};
392 
393     template<typename ClassT, typename ReturnT, typename... Args>
394     struct UnaryLambdaTraits<ReturnT( ClassT::* )( Args... ) const> {
395         static const bool isValid = false;
396     };
397 
398     template<typename ClassT, typename ReturnT, typename ArgT>
399     struct UnaryLambdaTraits<ReturnT( ClassT::* )( ArgT ) const> {
400         static const bool isValid = true;
401         using ArgType = typename std::remove_const<typename std::remove_reference<ArgT>::type>::type;
402         using ReturnType = ReturnT;
403     };
404 
405     class TokenStream;
406 
407     // Transport for raw args (copied from main args, or supplied via init list for testing)
408     class Args {
409         friend TokenStream;
410         std::string m_exeName;
411         std::vector<std::string> m_args;
412 
413     public:
Args(int argc,char const * const * argv)414         Args( int argc, char const* const* argv )
415             : m_exeName(argv[0]),
416               m_args(argv + 1, argv + argc) {}
417 
Args(std::initializer_list<std::string> args)418         Args( std::initializer_list<std::string> args )
419         :   m_exeName( *args.begin() ),
420             m_args( args.begin()+1, args.end() )
421         {}
422 
exeName() const423         auto exeName() const -> std::string {
424             return m_exeName;
425         }
426     };
427 
428     // Wraps a token coming from a token stream. These may not directly correspond to strings as a single string
429     // may encode an option + its argument if the : or = form is used
430     enum class TokenType {
431         Option, Argument
432     };
433     struct Token {
434         TokenType type;
435         std::string token;
436     };
437 
isOptPrefix(char c)438     inline auto isOptPrefix( char c ) -> bool {
439         return c == '-'
440 #ifdef CLARA_PLATFORM_WINDOWS
441             || c == '/'
442 #endif
443         ;
444     }
445 
446     // Abstracts iterators into args as a stream of tokens, with option arguments uniformly handled
447     class TokenStream {
448         using Iterator = std::vector<std::string>::const_iterator;
449         Iterator it;
450         Iterator itEnd;
451         std::vector<Token> m_tokenBuffer;
452 
loadBuffer()453         void loadBuffer() {
454             m_tokenBuffer.resize( 0 );
455 
456             // Skip any empty strings
457             while( it != itEnd && it->empty() )
458                 ++it;
459 
460             if( it != itEnd ) {
461                 auto const &next = *it;
462                 if( isOptPrefix( next[0] ) ) {
463                     auto delimiterPos = next.find_first_of( " :=" );
464                     if( delimiterPos != std::string::npos ) {
465                         m_tokenBuffer.push_back( { TokenType::Option, next.substr( 0, delimiterPos ) } );
466                         m_tokenBuffer.push_back( { TokenType::Argument, next.substr( delimiterPos + 1 ) } );
467                     } else {
468                         if( next[1] != '-' && next.size() > 2 ) {
469                             std::string opt = "- ";
470                             for( size_t i = 1; i < next.size(); ++i ) {
471                                 opt[1] = next[i];
472                                 m_tokenBuffer.push_back( { TokenType::Option, opt } );
473                             }
474                         } else {
475                             m_tokenBuffer.push_back( { TokenType::Option, next } );
476                         }
477                     }
478                 } else {
479                     m_tokenBuffer.push_back( { TokenType::Argument, next } );
480                 }
481             }
482         }
483 
484     public:
TokenStream(Args const & args)485         explicit TokenStream( Args const &args ) : TokenStream( args.m_args.begin(), args.m_args.end() ) {}
486 
TokenStream(Iterator it,Iterator itEnd)487         TokenStream( Iterator it, Iterator itEnd ) : it( it ), itEnd( itEnd ) {
488             loadBuffer();
489         }
490 
operator bool() const491         explicit operator bool() const {
492             return !m_tokenBuffer.empty() || it != itEnd;
493         }
494 
count() const495         auto count() const -> size_t { return m_tokenBuffer.size() + (itEnd - it); }
496 
operator *() const497         auto operator*() const -> Token {
498             assert( !m_tokenBuffer.empty() );
499             return m_tokenBuffer.front();
500         }
501 
operator ->() const502         auto operator->() const -> Token const * {
503             assert( !m_tokenBuffer.empty() );
504             return &m_tokenBuffer.front();
505         }
506 
operator ++()507         auto operator++() -> TokenStream & {
508             if( m_tokenBuffer.size() >= 2 ) {
509                 m_tokenBuffer.erase( m_tokenBuffer.begin() );
510             } else {
511                 if( it != itEnd )
512                     ++it;
513                 loadBuffer();
514             }
515             return *this;
516         }
517     };
518 
519 
520     class ResultBase {
521     public:
522         enum Type {
523             Ok, LogicError, RuntimeError
524         };
525 
526     protected:
ResultBase(Type type)527         ResultBase( Type type ) : m_type( type ) {}
528         virtual ~ResultBase() = default;
529 
530         virtual void enforceOk() const = 0;
531 
532         Type m_type;
533     };
534 
535     template<typename T>
536     class ResultValueBase : public ResultBase {
537     public:
value() const538         auto value() const -> T const & {
539             enforceOk();
540             return m_value;
541         }
542 
543     protected:
ResultValueBase(Type type)544         ResultValueBase( Type type ) : ResultBase( type ) {}
545 
ResultValueBase(ResultValueBase const & other)546         ResultValueBase( ResultValueBase const &other ) : ResultBase( other ) {
547             if( m_type == ResultBase::Ok )
548                 new( &m_value ) T( other.m_value );
549         }
550 
ResultValueBase(Type,T const & value)551         ResultValueBase( Type, T const &value ) : ResultBase( Ok ) {
552             new( &m_value ) T( value );
553         }
554 
operator =(ResultValueBase const & other)555         auto operator=( ResultValueBase const &other ) -> ResultValueBase & {
556             if( m_type == ResultBase::Ok )
557                 m_value.~T();
558             ResultBase::operator=(other);
559             if( m_type == ResultBase::Ok )
560                 new( &m_value ) T( other.m_value );
561             return *this;
562         }
563 
~ResultValueBase()564         ~ResultValueBase() override {
565             if( m_type == Ok )
566                 m_value.~T();
567         }
568 
569         union {
570             T m_value;
571         };
572     };
573 
574     template<>
575     class ResultValueBase<void> : public ResultBase {
576     protected:
577         using ResultBase::ResultBase;
578     };
579 
580     template<typename T = void>
581     class BasicResult : public ResultValueBase<T> {
582     public:
583         template<typename U>
BasicResult(BasicResult<U> const & other)584         explicit BasicResult( BasicResult<U> const &other )
585         :   ResultValueBase<T>( other.type() ),
586             m_errorMessage( other.errorMessage() )
587         {
588             assert( type() != ResultBase::Ok );
589         }
590 
591         template<typename U>
ok(U const & value)592         static auto ok( U const &value ) -> BasicResult { return { ResultBase::Ok, value }; }
ok()593         static auto ok() -> BasicResult { return { ResultBase::Ok }; }
logicError(std::string const & message)594         static auto logicError( std::string const &message ) -> BasicResult { return { ResultBase::LogicError, message }; }
runtimeError(std::string const & message)595         static auto runtimeError( std::string const &message ) -> BasicResult { return { ResultBase::RuntimeError, message }; }
596 
operator bool() const597         explicit operator bool() const { return m_type == ResultBase::Ok; }
type() const598         auto type() const -> ResultBase::Type { return m_type; }
errorMessage() const599         auto errorMessage() const -> std::string { return m_errorMessage; }
600 
601     protected:
enforceOk() const602         void enforceOk() const override {
603 
604             // Errors shouldn't reach this point, but if they do
605             // the actual error message will be in m_errorMessage
606             assert( m_type != ResultBase::LogicError );
607             assert( m_type != ResultBase::RuntimeError );
608             if( m_type != ResultBase::Ok )
609                 std::abort();
610         }
611 
612         std::string m_errorMessage; // Only populated if resultType is an error
613 
BasicResult(ResultBase::Type type,std::string const & message)614         BasicResult( ResultBase::Type type, std::string const &message )
615         :   ResultValueBase<T>(type),
616             m_errorMessage(message)
617         {
618             assert( m_type != ResultBase::Ok );
619         }
620 
621         using ResultValueBase<T>::ResultValueBase;
622         using ResultBase::m_type;
623     };
624 
625     enum class ParseResultType {
626         Matched, NoMatch, ShortCircuitAll, ShortCircuitSame
627     };
628 
629     class ParseState {
630     public:
631 
ParseState(ParseResultType type,TokenStream const & remainingTokens)632         ParseState( ParseResultType type, TokenStream const &remainingTokens )
633         : m_type(type),
634           m_remainingTokens( remainingTokens )
635         {}
636 
type() const637         auto type() const -> ParseResultType { return m_type; }
remainingTokens() const638         auto remainingTokens() const -> TokenStream { return m_remainingTokens; }
639 
640     private:
641         ParseResultType m_type;
642         TokenStream m_remainingTokens;
643     };
644 
645     using Result = BasicResult<void>;
646     using ParserResult = BasicResult<ParseResultType>;
647     using InternalParseResult = BasicResult<ParseState>;
648 
649     struct HelpColumns {
650         std::string left;
651         std::string right;
652     };
653 
654     template<typename T>
convertInto(std::string const & source,T & target)655     inline auto convertInto( std::string const &source, T& target ) -> ParserResult {
656         std::stringstream ss;
657         ss << source;
658         ss >> target;
659         if( ss.fail() )
660             return ParserResult::runtimeError( "Unable to convert '" + source + "' to destination type" );
661         else
662             return ParserResult::ok( ParseResultType::Matched );
663     }
convertInto(std::string const & source,std::string & target)664     inline auto convertInto( std::string const &source, std::string& target ) -> ParserResult {
665         target = source;
666         return ParserResult::ok( ParseResultType::Matched );
667     }
convertInto(std::string const & source,bool & target)668     inline auto convertInto( std::string const &source, bool &target ) -> ParserResult {
669         std::string srcLC = source;
670         std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( char c ) { return static_cast<char>( ::tolower(c) ); } );
671         if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on")
672             target = true;
673         else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" || srcLC == "off")
674             target = false;
675         else
676             return ParserResult::runtimeError( "Expected a boolean value but did not recognise: '" + source + "'" );
677         return ParserResult::ok( ParseResultType::Matched );
678     }
679 #ifdef CLARA_CONFIG_OPTIONAL_TYPE
680     template<typename T>
convertInto(std::string const & source,CLARA_CONFIG_OPTIONAL_TYPE<T> & target)681     inline auto convertInto( std::string const &source, CLARA_CONFIG_OPTIONAL_TYPE<T>& target ) -> ParserResult {
682         T temp;
683         auto result = convertInto( source, temp );
684         if( result )
685             target = std::move(temp);
686         return result;
687     }
688 #endif // CLARA_CONFIG_OPTIONAL_TYPE
689 
690     struct NonCopyable {
691         NonCopyable() = default;
692         NonCopyable( NonCopyable const & ) = delete;
693         NonCopyable( NonCopyable && ) = delete;
694         NonCopyable &operator=( NonCopyable const & ) = delete;
695         NonCopyable &operator=( NonCopyable && ) = delete;
696     };
697 
698     struct BoundRef : NonCopyable {
699         virtual ~BoundRef() = default;
isContainerclara::detail::BoundRef700         virtual auto isContainer() const -> bool { return false; }
isFlagclara::detail::BoundRef701         virtual auto isFlag() const -> bool { return false; }
702     };
703     struct BoundValueRefBase : BoundRef {
704         virtual auto setValue( std::string const &arg ) -> ParserResult = 0;
705     };
706     struct BoundFlagRefBase : BoundRef {
707         virtual auto setFlag( bool flag ) -> ParserResult = 0;
isFlagclara::detail::BoundFlagRefBase708         virtual auto isFlag() const -> bool { return true; }
709     };
710 
711     template<typename T>
712     struct BoundValueRef : BoundValueRefBase {
713         T &m_ref;
714 
BoundValueRefclara::detail::BoundValueRef715         explicit BoundValueRef( T &ref ) : m_ref( ref ) {}
716 
setValueclara::detail::BoundValueRef717         auto setValue( std::string const &arg ) -> ParserResult override {
718             return convertInto( arg, m_ref );
719         }
720     };
721 
722     template<typename T>
723     struct BoundValueRef<std::vector<T>> : BoundValueRefBase {
724         std::vector<T> &m_ref;
725 
BoundValueRefclara::detail::BoundValueRef726         explicit BoundValueRef( std::vector<T> &ref ) : m_ref( ref ) {}
727 
isContainerclara::detail::BoundValueRef728         auto isContainer() const -> bool override { return true; }
729 
setValueclara::detail::BoundValueRef730         auto setValue( std::string const &arg ) -> ParserResult override {
731             T temp;
732             auto result = convertInto( arg, temp );
733             if( result )
734                 m_ref.push_back( temp );
735             return result;
736         }
737     };
738 
739     struct BoundFlagRef : BoundFlagRefBase {
740         bool &m_ref;
741 
BoundFlagRefclara::detail::BoundFlagRef742         explicit BoundFlagRef( bool &ref ) : m_ref( ref ) {}
743 
setFlagclara::detail::BoundFlagRef744         auto setFlag( bool flag ) -> ParserResult override {
745             m_ref = flag;
746             return ParserResult::ok( ParseResultType::Matched );
747         }
748     };
749 
750     template<typename ReturnType>
751     struct LambdaInvoker {
752         static_assert( std::is_same<ReturnType, ParserResult>::value, "Lambda must return void or clara::ParserResult" );
753 
754         template<typename L, typename ArgType>
invokeclara::detail::LambdaInvoker755         static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult {
756             return lambda( arg );
757         }
758     };
759 
760     template<>
761     struct LambdaInvoker<void> {
762         template<typename L, typename ArgType>
invokeclara::detail::LambdaInvoker763         static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult {
764             lambda( arg );
765             return ParserResult::ok( ParseResultType::Matched );
766         }
767     };
768 
769     template<typename ArgType, typename L>
invokeLambda(L const & lambda,std::string const & arg)770     inline auto invokeLambda( L const &lambda, std::string const &arg ) -> ParserResult {
771         ArgType temp{};
772         auto result = convertInto( arg, temp );
773         return !result
774            ? result
775            : LambdaInvoker<typename UnaryLambdaTraits<L>::ReturnType>::invoke( lambda, temp );
776     }
777 
778 
779     template<typename L>
780     struct BoundLambda : BoundValueRefBase {
781         L m_lambda;
782 
783         static_assert( UnaryLambdaTraits<L>::isValid, "Supplied lambda must take exactly one argument" );
BoundLambdaclara::detail::BoundLambda784         explicit BoundLambda( L const &lambda ) : m_lambda( lambda ) {}
785 
setValueclara::detail::BoundLambda786         auto setValue( std::string const &arg ) -> ParserResult override {
787             return invokeLambda<typename UnaryLambdaTraits<L>::ArgType>( m_lambda, arg );
788         }
789     };
790 
791     template<typename L>
792     struct BoundFlagLambda : BoundFlagRefBase {
793         L m_lambda;
794 
795         static_assert( UnaryLambdaTraits<L>::isValid, "Supplied lambda must take exactly one argument" );
796         static_assert( std::is_same<typename UnaryLambdaTraits<L>::ArgType, bool>::value, "flags must be boolean" );
797 
BoundFlagLambdaclara::detail::BoundFlagLambda798         explicit BoundFlagLambda( L const &lambda ) : m_lambda( lambda ) {}
799 
setFlagclara::detail::BoundFlagLambda800         auto setFlag( bool flag ) -> ParserResult override {
801             return LambdaInvoker<typename UnaryLambdaTraits<L>::ReturnType>::invoke( m_lambda, flag );
802         }
803     };
804 
805     enum class Optionality { Optional, Required };
806 
807     struct Parser;
808 
809     class ParserBase {
810     public:
811         virtual ~ParserBase() = default;
validate() const812         virtual auto validate() const -> Result { return Result::ok(); }
813         virtual auto parse( std::string const& exeName, TokenStream const &tokens) const -> InternalParseResult  = 0;
cardinality() const814         virtual auto cardinality() const -> size_t { return 1; }
815 
parse(Args const & args) const816         auto parse( Args const &args ) const -> InternalParseResult {
817             return parse( args.exeName(), TokenStream( args ) );
818         }
819     };
820 
821     template<typename DerivedT>
822     class ComposableParserImpl : public ParserBase {
823     public:
824         template<typename T>
825         auto operator|( T const &other ) const -> Parser;
826 
827 		template<typename T>
828         auto operator+( T const &other ) const -> Parser;
829     };
830 
831     // Common code and state for Args and Opts
832     template<typename DerivedT>
833     class ParserRefImpl : public ComposableParserImpl<DerivedT> {
834     protected:
835         Optionality m_optionality = Optionality::Optional;
836         std::shared_ptr<BoundRef> m_ref;
837         std::string m_hint;
838         std::string m_description;
839 
ParserRefImpl(std::shared_ptr<BoundRef> const & ref)840         explicit ParserRefImpl( std::shared_ptr<BoundRef> const &ref ) : m_ref( ref ) {}
841 
842     public:
843         template<typename T>
ParserRefImpl(T & ref,std::string const & hint)844         ParserRefImpl( T &ref, std::string const &hint )
845         :   m_ref( std::make_shared<BoundValueRef<T>>( ref ) ),
846             m_hint( hint )
847         {}
848 
849         template<typename LambdaT>
ParserRefImpl(LambdaT const & ref,std::string const & hint)850         ParserRefImpl( LambdaT const &ref, std::string const &hint )
851         :   m_ref( std::make_shared<BoundLambda<LambdaT>>( ref ) ),
852             m_hint(hint)
853         {}
854 
operator ()(std::string const & description)855         auto operator()( std::string const &description ) -> DerivedT & {
856             m_description = description;
857             return static_cast<DerivedT &>( *this );
858         }
859 
optional()860         auto optional() -> DerivedT & {
861             m_optionality = Optionality::Optional;
862             return static_cast<DerivedT &>( *this );
863         };
864 
required()865         auto required() -> DerivedT & {
866             m_optionality = Optionality::Required;
867             return static_cast<DerivedT &>( *this );
868         };
869 
isOptional() const870         auto isOptional() const -> bool {
871             return m_optionality == Optionality::Optional;
872         }
873 
cardinality() const874         auto cardinality() const -> size_t override {
875             if( m_ref->isContainer() )
876                 return 0;
877             else
878                 return 1;
879         }
880 
hint() const881         auto hint() const -> std::string { return m_hint; }
882     };
883 
884     class ExeName : public ComposableParserImpl<ExeName> {
885         std::shared_ptr<std::string> m_name;
886         std::shared_ptr<BoundValueRefBase> m_ref;
887 
888         template<typename LambdaT>
makeRef(LambdaT const & lambda)889         static auto makeRef(LambdaT const &lambda) -> std::shared_ptr<BoundValueRefBase> {
890             return std::make_shared<BoundLambda<LambdaT>>( lambda) ;
891         }
892 
893     public:
ExeName()894         ExeName() : m_name( std::make_shared<std::string>( "<executable>" ) ) {}
895 
ExeName(std::string & ref)896         explicit ExeName( std::string &ref ) : ExeName() {
897             m_ref = std::make_shared<BoundValueRef<std::string>>( ref );
898         }
899 
900         template<typename LambdaT>
ExeName(LambdaT const & lambda)901         explicit ExeName( LambdaT const& lambda ) : ExeName() {
902             m_ref = std::make_shared<BoundLambda<LambdaT>>( lambda );
903         }
904 
905         // The exe name is not parsed out of the normal tokens, but is handled specially
parse(std::string const &,TokenStream const & tokens) const906         auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override {
907             return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) );
908         }
909 
name() const910         auto name() const -> std::string { return *m_name; }
set(std::string const & newName)911         auto set( std::string const& newName ) -> ParserResult {
912 
913             auto lastSlash = newName.find_last_of( "\\/" );
914             auto filename = ( lastSlash == std::string::npos )
915                     ? newName
916                     : newName.substr( lastSlash+1 );
917 
918             *m_name = filename;
919             if( m_ref )
920                 return m_ref->setValue( filename );
921             else
922                 return ParserResult::ok( ParseResultType::Matched );
923         }
924     };
925 
926     class Arg : public ParserRefImpl<Arg> {
927     public:
928         using ParserRefImpl::ParserRefImpl;
929 
parse(std::string const &,TokenStream const & tokens) const930         auto parse( std::string const &, TokenStream const &tokens ) const -> InternalParseResult override {
931             auto validationResult = validate();
932             if( !validationResult )
933                 return InternalParseResult( validationResult );
934 
935             auto remainingTokens = tokens;
936             auto const &token = *remainingTokens;
937             if( token.type != TokenType::Argument )
938                 return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) );
939 
940             assert( !m_ref->isFlag() );
941             auto valueRef = static_cast<detail::BoundValueRefBase*>( m_ref.get() );
942 
943             auto result = valueRef->setValue( remainingTokens->token );
944             if( !result )
945                 return InternalParseResult( result );
946             else
947                 return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) );
948         }
949     };
950 
normaliseOpt(std::string const & optName)951     inline auto normaliseOpt( std::string const &optName ) -> std::string {
952 #ifdef CLARA_PLATFORM_WINDOWS
953         if( optName[0] == '/' )
954             return "-" + optName.substr( 1 );
955         else
956 #endif
957             return optName;
958     }
959 
960     class Opt : public ParserRefImpl<Opt> {
961     protected:
962         std::vector<std::string> m_optNames;
963 
964     public:
965         template<typename LambdaT>
Opt(LambdaT const & ref)966         explicit Opt( LambdaT const &ref ) : ParserRefImpl( std::make_shared<BoundFlagLambda<LambdaT>>( ref ) ) {}
967 
Opt(bool & ref)968         explicit Opt( bool &ref ) : ParserRefImpl( std::make_shared<BoundFlagRef>( ref ) ) {}
969 
970         template<typename LambdaT>
Opt(LambdaT const & ref,std::string const & hint)971         Opt( LambdaT const &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {}
972 
973         template<typename T>
Opt(T & ref,std::string const & hint)974         Opt( T &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {}
975 
operator [](std::string const & optName)976         auto operator[]( std::string const &optName ) -> Opt & {
977             m_optNames.push_back( optName );
978             return *this;
979         }
980 
getHelpColumns() const981         auto getHelpColumns() const -> std::vector<HelpColumns> {
982             std::ostringstream oss;
983             bool first = true;
984             for( auto const &opt : m_optNames ) {
985                 if (first)
986                     first = false;
987                 else
988                     oss << ", ";
989                 oss << opt;
990             }
991             if( !m_hint.empty() )
992                 oss << " <" << m_hint << ">";
993             return { { oss.str(), m_description } };
994         }
995 
isMatch(std::string const & optToken) const996         auto isMatch( std::string const &optToken ) const -> bool {
997             auto normalisedToken = normaliseOpt( optToken );
998             for( auto const &name : m_optNames ) {
999                 if( normaliseOpt( name ) == normalisedToken )
1000                     return true;
1001             }
1002             return false;
1003         }
1004 
1005         using ParserBase::parse;
1006 
parse(std::string const &,TokenStream const & tokens) const1007         auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override {
1008             auto validationResult = validate();
1009             if( !validationResult )
1010                 return InternalParseResult( validationResult );
1011 
1012             auto remainingTokens = tokens;
1013             if( remainingTokens && remainingTokens->type == TokenType::Option ) {
1014                 auto const &token = *remainingTokens;
1015                 if( isMatch(token.token ) ) {
1016                     if( m_ref->isFlag() ) {
1017                         auto flagRef = static_cast<detail::BoundFlagRefBase*>( m_ref.get() );
1018                         auto result = flagRef->setFlag( true );
1019                         if( !result )
1020                             return InternalParseResult( result );
1021                         if( result.value() == ParseResultType::ShortCircuitAll )
1022                             return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) );
1023                     } else {
1024                         auto valueRef = static_cast<detail::BoundValueRefBase*>( m_ref.get() );
1025                         ++remainingTokens;
1026                         if( !remainingTokens )
1027                             return InternalParseResult::runtimeError( "Expected argument following " + token.token );
1028                         auto const &argToken = *remainingTokens;
1029                         if( argToken.type != TokenType::Argument )
1030                             return InternalParseResult::runtimeError( "Expected argument following " + token.token );
1031                         auto result = valueRef->setValue( argToken.token );
1032                         if( !result )
1033                             return InternalParseResult( result );
1034                         if( result.value() == ParseResultType::ShortCircuitAll )
1035                             return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) );
1036                     }
1037                     return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) );
1038                 }
1039             }
1040             return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) );
1041         }
1042 
validate() const1043         auto validate() const -> Result override {
1044             if( m_optNames.empty() )
1045                 return Result::logicError( "No options supplied to Opt" );
1046             for( auto const &name : m_optNames ) {
1047                 if( name.empty() )
1048                     return Result::logicError( "Option name cannot be empty" );
1049 #ifdef CLARA_PLATFORM_WINDOWS
1050                 if( name[0] != '-' && name[0] != '/' )
1051                     return Result::logicError( "Option name must begin with '-' or '/'" );
1052 #else
1053                 if( name[0] != '-' )
1054                     return Result::logicError( "Option name must begin with '-'" );
1055 #endif
1056             }
1057             return ParserRefImpl::validate();
1058         }
1059     };
1060 
1061     struct Help : Opt {
Helpclara::detail::Help1062         Help( bool &showHelpFlag )
1063         :   Opt([&]( bool flag ) {
1064                 showHelpFlag = flag;
1065                 return ParserResult::ok( ParseResultType::ShortCircuitAll );
1066             })
1067         {
1068             static_cast<Opt &>( *this )
1069                     ("display usage information")
1070                     ["-?"]["-h"]["--help"]
1071                     .optional();
1072         }
1073     };
1074 
1075 
1076     struct Parser : ParserBase {
1077 
1078         mutable ExeName m_exeName;
1079         std::vector<Opt> m_options;
1080         std::vector<Arg> m_args;
1081 
operator |=clara::detail::Parser1082         auto operator|=( ExeName const &exeName ) -> Parser & {
1083             m_exeName = exeName;
1084             return *this;
1085         }
1086 
operator |=clara::detail::Parser1087         auto operator|=( Arg const &arg ) -> Parser & {
1088             m_args.push_back(arg);
1089             return *this;
1090         }
1091 
operator |=clara::detail::Parser1092         auto operator|=( Opt const &opt ) -> Parser & {
1093             m_options.push_back(opt);
1094             return *this;
1095         }
1096 
operator |=clara::detail::Parser1097         auto operator|=( Parser const &other ) -> Parser & {
1098             m_options.insert(m_options.end(), other.m_options.begin(), other.m_options.end());
1099             m_args.insert(m_args.end(), other.m_args.begin(), other.m_args.end());
1100             return *this;
1101         }
1102 
1103         template<typename T>
operator |clara::detail::Parser1104         auto operator|( T const &other ) const -> Parser {
1105             return Parser( *this ) |= other;
1106         }
1107 
1108         // Forward deprecated interface with '+' instead of '|'
1109         template<typename T>
operator +=clara::detail::Parser1110         auto operator+=( T const &other ) -> Parser & { return operator|=( other ); }
1111         template<typename T>
operator +clara::detail::Parser1112         auto operator+( T const &other ) const -> Parser { return operator|( other ); }
1113 
getHelpColumnsclara::detail::Parser1114         auto getHelpColumns() const -> std::vector<HelpColumns> {
1115             std::vector<HelpColumns> cols;
1116             for (auto const &o : m_options) {
1117                 auto childCols = o.getHelpColumns();
1118                 cols.insert( cols.end(), childCols.begin(), childCols.end() );
1119             }
1120             return cols;
1121         }
1122 
writeToStreamclara::detail::Parser1123         void writeToStream( std::ostream &os ) const {
1124             if (!m_exeName.name().empty()) {
1125                 os << "usage:\n" << "  " << m_exeName.name() << " ";
1126                 bool required = true, first = true;
1127                 for( auto const &arg : m_args ) {
1128                     if (first)
1129                         first = false;
1130                     else
1131                         os << " ";
1132                     if( arg.isOptional() && required ) {
1133                         os << "[";
1134                         required = false;
1135                     }
1136                     os << "<" << arg.hint() << ">";
1137                     if( arg.cardinality() == 0 )
1138                         os << " ... ";
1139                 }
1140                 if( !required )
1141                     os << "]";
1142                 if( !m_options.empty() )
1143                     os << " options";
1144                 os << "\n\nwhere options are:" << std::endl;
1145             }
1146 
1147             auto rows = getHelpColumns();
1148             size_t consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH;
1149             size_t optWidth = 0;
1150             for( auto const &cols : rows )
1151                 optWidth = (std::max)(optWidth, cols.left.size() + 2);
1152 
1153             optWidth = (std::min)(optWidth, consoleWidth/2);
1154 
1155             for( auto const &cols : rows ) {
1156                 auto row =
1157                         TextFlow::Column( cols.left ).width( optWidth ).indent( 2 ) +
1158                         TextFlow::Spacer(4) +
1159                         TextFlow::Column( cols.right ).width( consoleWidth - 7 - optWidth );
1160                 os << row << std::endl;
1161             }
1162         }
1163 
operator <<(std::ostream & os,Parser const & parser)1164         friend auto operator<<( std::ostream &os, Parser const &parser ) -> std::ostream& {
1165             parser.writeToStream( os );
1166             return os;
1167         }
1168 
validateclara::detail::Parser1169         auto validate() const -> Result override {
1170             for( auto const &opt : m_options ) {
1171                 auto result = opt.validate();
1172                 if( !result )
1173                     return result;
1174             }
1175             for( auto const &arg : m_args ) {
1176                 auto result = arg.validate();
1177                 if( !result )
1178                     return result;
1179             }
1180             return Result::ok();
1181         }
1182 
1183         using ParserBase::parse;
1184 
parseclara::detail::Parser1185         auto parse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult override {
1186 
1187             struct ParserInfo {
1188                 ParserBase const* parser = nullptr;
1189                 size_t count = 0;
1190             };
1191             const size_t totalParsers = m_options.size() + m_args.size();
1192             assert( totalParsers < 512 );
1193             // ParserInfo parseInfos[totalParsers]; // <-- this is what we really want to do
1194             ParserInfo parseInfos[512];
1195 
1196             {
1197                 size_t i = 0;
1198                 for (auto const &opt : m_options) parseInfos[i++].parser = &opt;
1199                 for (auto const &arg : m_args) parseInfos[i++].parser = &arg;
1200             }
1201 
1202             m_exeName.set( exeName );
1203 
1204             auto result = InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) );
1205             while( result.value().remainingTokens() ) {
1206                 bool tokenParsed = false;
1207 
1208                 for( size_t i = 0; i < totalParsers; ++i ) {
1209                     auto&  parseInfo = parseInfos[i];
1210                     if( parseInfo.parser->cardinality() == 0 || parseInfo.count < parseInfo.parser->cardinality() ) {
1211                         result = parseInfo.parser->parse(exeName, result.value().remainingTokens());
1212                         if (!result)
1213                             return result;
1214                         if (result.value().type() != ParseResultType::NoMatch) {
1215                             tokenParsed = true;
1216                             ++parseInfo.count;
1217                             break;
1218                         }
1219                     }
1220                 }
1221 
1222                 if( result.value().type() == ParseResultType::ShortCircuitAll )
1223                     return result;
1224                 if( !tokenParsed )
1225                     return InternalParseResult::runtimeError( "Unrecognised token: " + result.value().remainingTokens()->token );
1226             }
1227             // !TBD Check missing required options
1228             return result;
1229         }
1230     };
1231 
1232     template<typename DerivedT>
1233     template<typename T>
operator |(T const & other) const1234     auto ComposableParserImpl<DerivedT>::operator|( T const &other ) const -> Parser {
1235         return Parser() | static_cast<DerivedT const &>( *this ) | other;
1236     }
1237 } // namespace detail
1238 
1239 
1240 // A Combined parser
1241 using detail::Parser;
1242 
1243 // A parser for options
1244 using detail::Opt;
1245 
1246 // A parser for arguments
1247 using detail::Arg;
1248 
1249 // Wrapper for argc, argv from main()
1250 using detail::Args;
1251 
1252 // Specifies the name of the executable
1253 using detail::ExeName;
1254 
1255 // Convenience wrapper for option parser that specifies the help option
1256 using detail::Help;
1257 
1258 // enum of result types from a parse
1259 using detail::ParseResultType;
1260 
1261 // Result type for parser operation
1262 using detail::ParserResult;
1263 
1264 
1265 } // namespace clara
1266 
1267 #endif // CLARA_HPP_INCLUDED
1268