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