1 /* 2 * Created by Phil on 25/05/2013. 3 * Copyright 2013 Two Blue Cubes Ltd. All rights reserved. 4 * 5 * Distributed under the Boost Software License, Version 1.0. (See accompanying 6 * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 */ 8 9 // Version 0.0.2.4 10 11 // Only use header guard if we are not using an outer namespace 12 #if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE) 13 14 #ifndef STITCH_CLARA_OPEN_NAMESPACE 15 #define TWOBLUECUBES_CLARA_H_INCLUDED 16 #define STITCH_CLARA_OPEN_NAMESPACE 17 #define STITCH_CLARA_CLOSE_NAMESPACE 18 #else 19 #define STITCH_CLARA_CLOSE_NAMESPACE } 20 #endif 21 22 23 #define STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE 24 25 // ----------- #included from tbc_text_format.h ----------- 26 27 /* 28 * Created by Phil on 18/4/2013. 29 * Copyright 2013 Two Blue Cubes Ltd. All rights reserved. 30 * 31 * Distributed under the Boost Software License, Version 1.0. (See accompanying 32 * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 33 */ 34 // Only use header guard if we are not using an outer namespace 35 #if !defined(TBC_TEXT_FORMAT_H_INCLUDED) || defined(STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE) 36 #ifndef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE 37 #define TBC_TEXT_FORMAT_H_INCLUDED 38 #endif 39 40 #include <string> 41 #include <vector> 42 #include <sstream> 43 #include <algorithm> 44 #include <cctype> 45 46 // Use optional outer namespace 47 #ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE 48 namespace STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE { 49 #endif 50 51 namespace Tbc { 52 53 #ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH 54 const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; 55 #else 56 const unsigned int consoleWidth = 80; 57 #endif 58 59 struct TextAttributes { TextAttributesTextAttributes60 TextAttributes() 61 : initialIndent( std::string::npos ), 62 indent( 0 ), 63 width( consoleWidth-1 ), 64 tabChar( '\t' ) 65 {} 66 setInitialIndentTextAttributes67 TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } setIndentTextAttributes68 TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } setWidthTextAttributes69 TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } setTabCharTextAttributes70 TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; } 71 72 std::size_t initialIndent; // indent of first line, or npos 73 std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos 74 std::size_t width; // maximum width of text, including indent. Longer text will wrap 75 char tabChar; // If this char is seen the indent is changed to current pos 76 }; 77 78 class Text { 79 public: 80 Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) attr(_attr)81 : attr( _attr ) 82 { 83 std::string wrappableChars = " [({.,/|\\-"; 84 std::size_t indent = _attr.initialIndent != std::string::npos 85 ? _attr.initialIndent 86 : _attr.indent; 87 std::string remainder = _str; 88 89 while( !remainder.empty() ) { 90 if( lines.size() >= 1000 ) { 91 lines.push_back( "... message truncated due to excessive size" ); 92 return; 93 } 94 std::size_t tabPos = std::string::npos; 95 std::size_t width = (std::min)( remainder.size(), _attr.width - indent ); 96 std::size_t pos = remainder.find_first_of( '\n' ); 97 if( pos <= width ) { 98 width = pos; 99 } 100 pos = remainder.find_last_of( _attr.tabChar, width ); 101 if( pos != std::string::npos ) { 102 tabPos = pos; 103 if( remainder[width] == '\n' ) 104 width--; 105 remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 ); 106 } 107 108 if( width == remainder.size() ) { 109 spliceLine( indent, remainder, width ); 110 } 111 else if( remainder[width] == '\n' ) { 112 spliceLine( indent, remainder, width ); 113 if( width <= 1 || remainder.size() != 1 ) 114 remainder = remainder.substr( 1 ); 115 indent = _attr.indent; 116 } 117 else { 118 pos = remainder.find_last_of( wrappableChars, width ); 119 if( pos != std::string::npos && pos > 0 ) { 120 spliceLine( indent, remainder, pos ); 121 if( remainder[0] == ' ' ) 122 remainder = remainder.substr( 1 ); 123 } 124 else { 125 spliceLine( indent, remainder, width-1 ); 126 lines.back() += "-"; 127 } 128 if( lines.size() == 1 ) 129 indent = _attr.indent; 130 if( tabPos != std::string::npos ) 131 indent += tabPos; 132 } 133 } 134 } 135 spliceLine(std::size_t _indent,std::string & _remainder,std::size_t _pos)136 void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) { 137 lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) ); 138 _remainder = _remainder.substr( _pos ); 139 } 140 141 typedef std::vector<std::string>::const_iterator const_iterator; 142 begin()143 const_iterator begin() const { return lines.begin(); } end()144 const_iterator end() const { return lines.end(); } last()145 std::string const& last() const { return lines.back(); } size()146 std::size_t size() const { return lines.size(); } 147 std::string const& operator[]( std::size_t _index ) const { return lines[_index]; } toString()148 std::string toString() const { 149 std::ostringstream oss; 150 oss << *this; 151 return oss.str(); 152 } 153 154 friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { 155 for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); 156 it != itEnd; ++it ) { 157 if( it != _text.begin() ) 158 _stream << "\n"; 159 _stream << *it; 160 } 161 return _stream; 162 } 163 164 private: 165 std::string str; 166 TextAttributes attr; 167 std::vector<std::string> lines; 168 }; 169 170 } // end namespace Tbc 171 172 #ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE 173 } // end outer namespace 174 #endif 175 176 #endif // TBC_TEXT_FORMAT_H_INCLUDED 177 178 // ----------- end of #include from tbc_text_format.h ----------- 179 // ........... back in clara.h 180 181 #undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE 182 183 184 // ----------- #included from clara_compilers.h ----------- 185 186 /* 187 * Created by Phil on 10/02/2016. 188 * Copyright 2016 Two Blue Cubes Ltd. All rights reserved. 189 * 190 * Distributed under the Boost Software License, Version 1.0. (See accompanying 191 * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 192 */ 193 #ifndef TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED 194 #define TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED 195 196 // Detect a number of compiler features - mostly C++11/14 conformance - by compiler 197 // The following features are defined: 198 // 199 // CLARA_CONFIG_CPP11_NULLPTR : is nullptr supported? 200 // CLARA_CONFIG_CPP11_NOEXCEPT : is noexcept supported? 201 // CLARA_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods 202 // CLARA_CONFIG_CPP11_OVERRIDE : is override supported? 203 // CLARA_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr) 204 205 // CLARA_CONFIG_CPP11_OR_GREATER : Is C++11 supported? 206 207 // CLARA_CONFIG_VARIADIC_MACROS : are variadic macros supported? 208 209 // In general each macro has a _NO_<feature name> form 210 // (e.g. CLARA_CONFIG_CPP11_NO_NULLPTR) which disables the feature. 211 // Many features, at point of detection, define an _INTERNAL_ macro, so they 212 // can be combined, en-mass, with the _NO_ forms later. 213 214 // All the C++11 features can be disabled with CLARA_CONFIG_NO_CPP11 215 216 #ifdef __clang__ 217 218 #if __has_feature(cxx_nullptr) 219 #define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR 220 #endif 221 222 #if __has_feature(cxx_noexcept) 223 #define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT 224 #endif 225 226 #endif // __clang__ 227 228 //////////////////////////////////////////////////////////////////////////////// 229 // GCC 230 #ifdef __GNUC__ 231 232 #if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) 233 #define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR 234 #endif 235 236 // - otherwise more recent versions define __cplusplus >= 201103L 237 // and will get picked up below 238 239 #endif // __GNUC__ 240 241 //////////////////////////////////////////////////////////////////////////////// 242 // Visual C++ 243 #ifdef _MSC_VER 244 245 #if (_MSC_VER >= 1600) 246 #define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR 247 #define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR 248 #endif 249 250 #if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015)) 251 #define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT 252 #define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS 253 #endif 254 255 #endif // _MSC_VER 256 257 258 //////////////////////////////////////////////////////////////////////////////// 259 // C++ language feature support 260 261 // catch all support for C++11 262 #if defined(__cplusplus) && __cplusplus >= 201103L 263 264 #define CLARA_CPP11_OR_GREATER 265 266 #if !defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR) 267 #define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR 268 #endif 269 270 #ifndef CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT 271 #define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT 272 #endif 273 274 #ifndef CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS 275 #define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS 276 #endif 277 278 #if !defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE) 279 #define CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE 280 #endif 281 #if !defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) 282 #define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR 283 #endif 284 285 286 #endif // __cplusplus >= 201103L 287 288 // Now set the actual defines based on the above + anything the user has configured 289 #if defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NO_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_NO_CPP11) 290 #define CLARA_CONFIG_CPP11_NULLPTR 291 #endif 292 #if defined(CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_NO_CPP11) 293 #define CLARA_CONFIG_CPP11_NOEXCEPT 294 #endif 295 #if defined(CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_NO_CPP11) 296 #define CLARA_CONFIG_CPP11_GENERATED_METHODS 297 #endif 298 #if defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_OVERRIDE) && !defined(CLARA_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_CPP11) 299 #define CLARA_CONFIG_CPP11_OVERRIDE 300 #endif 301 #if defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_UNIQUE_PTR) && !defined(CLARA_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_CPP11) 302 #define CLARA_CONFIG_CPP11_UNIQUE_PTR 303 #endif 304 305 306 // noexcept support: 307 #if defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_NOEXCEPT) 308 #define CLARA_NOEXCEPT noexcept 309 # define CLARA_NOEXCEPT_IS(x) noexcept(x) 310 #else 311 #define CLARA_NOEXCEPT throw() 312 # define CLARA_NOEXCEPT_IS(x) 313 #endif 314 315 // nullptr support 316 #ifdef CLARA_CONFIG_CPP11_NULLPTR 317 #define CLARA_NULL nullptr 318 #else 319 #define CLARA_NULL NULL 320 #endif 321 322 // override support 323 #ifdef CLARA_CONFIG_CPP11_OVERRIDE 324 #define CLARA_OVERRIDE override 325 #else 326 #define CLARA_OVERRIDE 327 #endif 328 329 // unique_ptr support 330 #ifdef CLARA_CONFIG_CPP11_UNIQUE_PTR 331 # define CLARA_AUTO_PTR( T ) std::unique_ptr<T> 332 #else 333 # define CLARA_AUTO_PTR( T ) std::auto_ptr<T> 334 #endif 335 336 #endif // TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED 337 338 339 // ----------- end of #include from clara_compilers.h ----------- 340 // ........... back in clara.h 341 342 343 #include <map> 344 #include <stdexcept> 345 #include <memory> 346 347 #if defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) 348 #define CLARA_PLATFORM_WINDOWS 349 #endif 350 351 352 // Use optional outer namespace 353 #ifdef STITCH_CLARA_OPEN_NAMESPACE 354 STITCH_CLARA_OPEN_NAMESPACE 355 #endif 356 357 namespace Clara { 358 359 struct UnpositionalTag {}; 360 361 extern UnpositionalTag _; 362 363 #ifdef CLARA_CONFIG_MAIN 364 UnpositionalTag _; 365 #endif 366 367 namespace Detail { 368 369 #ifdef CLARA_CONSOLE_WIDTH 370 const unsigned int consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH; 371 #else 372 const unsigned int consoleWidth = 80; 373 #endif 374 375 using namespace Tbc; 376 startsWith(std::string const & str,std::string const & prefix)377 inline bool startsWith( std::string const& str, std::string const& prefix ) { 378 return str.size() >= prefix.size() && str.substr( 0, prefix.size() ) == prefix; 379 } 380 381 template<typename T> struct RemoveConstRef{ typedef T type; }; 382 template<typename T> struct RemoveConstRef<T&>{ typedef T type; }; 383 template<typename T> struct RemoveConstRef<T const&>{ typedef T type; }; 384 template<typename T> struct RemoveConstRef<T const>{ typedef T type; }; 385 386 template<typename T> struct IsBool { static const bool value = false; }; 387 template<> struct IsBool<bool> { static const bool value = true; }; 388 389 template<typename T> 390 void convertInto( std::string const& _source, T& _dest ) { 391 std::stringstream ss; 392 ss << _source; 393 ss >> _dest; 394 if( ss.fail() ) 395 throw std::runtime_error( "Unable to convert " + _source + " to destination type" ); 396 } 397 inline void convertInto( std::string const& _source, std::string& _dest ) { 398 _dest = _source; 399 } 400 char toLowerCh(char c) { 401 return static_cast<char>( std::tolower( c ) ); 402 } 403 inline void convertInto( std::string const& _source, bool& _dest ) { 404 std::string sourceLC = _source; 405 std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), toLowerCh ); 406 if( sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on" ) 407 _dest = true; 408 else if( sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off" ) 409 _dest = false; 410 else 411 throw std::runtime_error( "Expected a boolean value but did not recognise:\n '" + _source + "'" ); 412 } 413 414 415 template<typename ConfigT> 416 struct IArgFunction { 417 virtual ~IArgFunction() {} 418 #ifdef CLARA_CONFIG_CPP11_GENERATED_METHODS 419 IArgFunction() = default; 420 IArgFunction( IArgFunction const& ) = default; 421 #endif 422 virtual void set( ConfigT& config, std::string const& value ) const = 0; 423 virtual bool takesArg() const = 0; 424 virtual IArgFunction* clone() const = 0; 425 }; 426 427 template<typename ConfigT> 428 class BoundArgFunction { 429 public: 430 BoundArgFunction() : functionObj( CLARA_NULL ) {} 431 BoundArgFunction( IArgFunction<ConfigT>* _functionObj ) : functionObj( _functionObj ) {} 432 BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : CLARA_NULL ) {} 433 BoundArgFunction& operator = ( BoundArgFunction const& other ) { 434 IArgFunction<ConfigT>* newFunctionObj = other.functionObj ? other.functionObj->clone() : CLARA_NULL; 435 delete functionObj; 436 functionObj = newFunctionObj; 437 return *this; 438 } 439 ~BoundArgFunction() { delete functionObj; } 440 441 void set( ConfigT& config, std::string const& value ) const { 442 functionObj->set( config, value ); 443 } 444 bool takesArg() const { return functionObj->takesArg(); } 445 446 bool isSet() const { 447 return functionObj != CLARA_NULL; 448 } 449 private: 450 IArgFunction<ConfigT>* functionObj; 451 }; 452 453 454 template<typename C> 455 struct NullBinder : IArgFunction<C>{ 456 virtual void set( C&, std::string const& ) const {} 457 virtual bool takesArg() const { return true; } 458 virtual IArgFunction<C>* clone() const { return new NullBinder( *this ); } 459 }; 460 461 template<typename C, typename M> 462 struct BoundDataMember : IArgFunction<C>{ 463 BoundDataMember( M C::* _member ) : member( _member ) {} 464 virtual void set( C& p, std::string const& stringValue ) const { 465 convertInto( stringValue, p.*member ); 466 } 467 virtual bool takesArg() const { return !IsBool<M>::value; } 468 virtual IArgFunction<C>* clone() const { return new BoundDataMember( *this ); } 469 M C::* member; 470 }; 471 template<typename C, typename M> 472 struct BoundUnaryMethod : IArgFunction<C>{ 473 BoundUnaryMethod( void (C::*_member)( M ) ) : member( _member ) {} 474 virtual void set( C& p, std::string const& stringValue ) const { 475 typename RemoveConstRef<M>::type value; 476 convertInto( stringValue, value ); 477 (p.*member)( value ); 478 } 479 virtual bool takesArg() const { return !IsBool<M>::value; } 480 virtual IArgFunction<C>* clone() const { return new BoundUnaryMethod( *this ); } 481 void (C::*member)( M ); 482 }; 483 template<typename C> 484 struct BoundNullaryMethod : IArgFunction<C>{ 485 BoundNullaryMethod( void (C::*_member)() ) : member( _member ) {} 486 virtual void set( C& p, std::string const& stringValue ) const { 487 bool value; 488 convertInto( stringValue, value ); 489 if( value ) 490 (p.*member)(); 491 } 492 virtual bool takesArg() const { return false; } 493 virtual IArgFunction<C>* clone() const { return new BoundNullaryMethod( *this ); } 494 void (C::*member)(); 495 }; 496 497 template<typename C> 498 struct BoundUnaryFunction : IArgFunction<C>{ 499 BoundUnaryFunction( void (*_function)( C& ) ) : function( _function ) {} 500 virtual void set( C& obj, std::string const& stringValue ) const { 501 bool value; 502 convertInto( stringValue, value ); 503 if( value ) 504 function( obj ); 505 } 506 virtual bool takesArg() const { return false; } 507 virtual IArgFunction<C>* clone() const { return new BoundUnaryFunction( *this ); } 508 void (*function)( C& ); 509 }; 510 511 template<typename C, typename T> 512 struct BoundBinaryFunction : IArgFunction<C>{ 513 BoundBinaryFunction( void (*_function)( C&, T ) ) : function( _function ) {} 514 virtual void set( C& obj, std::string const& stringValue ) const { 515 typename RemoveConstRef<T>::type value; 516 convertInto( stringValue, value ); 517 function( obj, value ); 518 } 519 virtual bool takesArg() const { return !IsBool<T>::value; } 520 virtual IArgFunction<C>* clone() const { return new BoundBinaryFunction( *this ); } 521 void (*function)( C&, T ); 522 }; 523 524 } // namespace Detail 525 526 inline std::vector<std::string> argsToVector( int argc, char const* const* const argv ) { 527 std::vector<std::string> args( static_cast<std::size_t>( argc ) ); 528 for( std::size_t i = 0; i < static_cast<std::size_t>( argc ); ++i ) 529 args[i] = argv[i]; 530 531 return args; 532 } 533 534 class Parser { 535 enum Mode { None, MaybeShortOpt, SlashOpt, ShortOpt, LongOpt, Positional }; 536 Mode mode; 537 std::size_t from; 538 bool inQuotes; 539 public: 540 541 struct Token { 542 enum Type { Positional, ShortOpt, LongOpt }; 543 Token( Type _type, std::string const& _data ) : type( _type ), data( _data ) {} 544 Type type; 545 std::string data; 546 }; 547 548 Parser() : mode( None ), from( 0 ), inQuotes( false ){} 549 550 void parseIntoTokens( std::vector<std::string> const& args, std::vector<Token>& tokens ) { 551 const std::string doubleDash = "--"; 552 for( std::size_t i = 1; i < args.size() && args[i] != doubleDash; ++i ) 553 parseIntoTokens( args[i], tokens); 554 } 555 556 void parseIntoTokens( std::string const& arg, std::vector<Token>& tokens ) { 557 for( std::size_t i = 0; i < arg.size(); ++i ) { 558 char c = arg[i]; 559 if( c == '"' ) 560 inQuotes = !inQuotes; 561 mode = handleMode( i, c, arg, tokens ); 562 } 563 mode = handleMode( arg.size(), '\0', arg, tokens ); 564 } 565 Mode handleMode( std::size_t i, char c, std::string const& arg, std::vector<Token>& tokens ) { 566 switch( mode ) { 567 case None: return handleNone( i, c ); 568 case MaybeShortOpt: return handleMaybeShortOpt( i, c ); 569 case ShortOpt: 570 case LongOpt: 571 case SlashOpt: return handleOpt( i, c, arg, tokens ); 572 case Positional: return handlePositional( i, c, arg, tokens ); 573 default: throw std::logic_error( "Unknown mode" ); 574 } 575 } 576 577 Mode handleNone( std::size_t i, char c ) { 578 if( inQuotes ) { 579 from = i; 580 return Positional; 581 } 582 switch( c ) { 583 case '-': return MaybeShortOpt; 584 #ifdef CLARA_PLATFORM_WINDOWS 585 case '/': from = i+1; return SlashOpt; 586 #endif 587 default: from = i; return Positional; 588 } 589 } 590 Mode handleMaybeShortOpt( std::size_t i, char c ) { 591 switch( c ) { 592 case '-': from = i+1; return LongOpt; 593 default: from = i; return ShortOpt; 594 } 595 } 596 597 Mode handleOpt( std::size_t i, char c, std::string const& arg, std::vector<Token>& tokens ) { 598 if( std::string( ":=\0", 3 ).find( c ) == std::string::npos ) 599 return mode; 600 601 std::string optName = arg.substr( from, i-from ); 602 if( mode == ShortOpt ) 603 for( std::size_t j = 0; j < optName.size(); ++j ) 604 tokens.push_back( Token( Token::ShortOpt, optName.substr( j, 1 ) ) ); 605 else if( mode == SlashOpt && optName.size() == 1 ) 606 tokens.push_back( Token( Token::ShortOpt, optName ) ); 607 else 608 tokens.push_back( Token( Token::LongOpt, optName ) ); 609 return None; 610 } 611 Mode handlePositional( std::size_t i, char c, std::string const& arg, std::vector<Token>& tokens ) { 612 if( inQuotes || std::string( "\0", 1 ).find( c ) == std::string::npos ) 613 return mode; 614 615 std::string data = arg.substr( from, i-from ); 616 tokens.push_back( Token( Token::Positional, data ) ); 617 return None; 618 } 619 }; 620 621 template<typename ConfigT> 622 struct CommonArgProperties { 623 CommonArgProperties() {} 624 CommonArgProperties( Detail::BoundArgFunction<ConfigT> const& _boundField ) : boundField( _boundField ) {} 625 626 Detail::BoundArgFunction<ConfigT> boundField; 627 std::string description; 628 std::string detail; 629 std::string placeholder; // Only value if boundField takes an arg 630 631 bool takesArg() const { 632 return !placeholder.empty(); 633 } 634 void validate() const { 635 if( !boundField.isSet() ) 636 throw std::logic_error( "option not bound" ); 637 } 638 }; 639 struct OptionArgProperties { 640 std::vector<std::string> shortNames; 641 std::string longName; 642 643 bool hasShortName( std::string const& shortName ) const { 644 return std::find( shortNames.begin(), shortNames.end(), shortName ) != shortNames.end(); 645 } 646 bool hasLongName( std::string const& _longName ) const { 647 return _longName == longName; 648 } 649 }; 650 struct PositionalArgProperties { 651 PositionalArgProperties() : position( -1 ) {} 652 int position; // -1 means non-positional (floating) 653 654 bool isFixedPositional() const { 655 return position != -1; 656 } 657 }; 658 659 template<typename ConfigT> 660 class CommandLine { 661 662 struct Arg : CommonArgProperties<ConfigT>, OptionArgProperties, PositionalArgProperties { 663 Arg() {} 664 Arg( Detail::BoundArgFunction<ConfigT> const& _boundField ) : CommonArgProperties<ConfigT>( _boundField ) {} 665 666 using CommonArgProperties<ConfigT>::placeholder; // !TBD 667 668 std::string dbgName() const { 669 if( !longName.empty() ) 670 return "--" + longName; 671 if( !shortNames.empty() ) 672 return "-" + shortNames[0]; 673 return "positional args"; 674 } 675 std::string commands() const { 676 std::ostringstream oss; 677 bool first = true; 678 std::vector<std::string>::const_iterator it = shortNames.begin(), itEnd = shortNames.end(); 679 for(; it != itEnd; ++it ) { 680 if( first ) 681 first = false; 682 else 683 oss << ", "; 684 oss << "-" << *it; 685 } 686 if( !longName.empty() ) { 687 if( !first ) 688 oss << ", "; 689 oss << "--" << longName; 690 } 691 if( !placeholder.empty() ) 692 oss << " <" << placeholder << ">"; 693 return oss.str(); 694 } 695 }; 696 697 typedef CLARA_AUTO_PTR( Arg ) ArgAutoPtr; 698 699 friend void addOptName( Arg& arg, std::string const& optName ) 700 { 701 if( optName.empty() ) 702 return; 703 if( Detail::startsWith( optName, "--" ) ) { 704 if( !arg.longName.empty() ) 705 throw std::logic_error( "Only one long opt may be specified. '" 706 + arg.longName 707 + "' already specified, now attempting to add '" 708 + optName + "'" ); 709 arg.longName = optName.substr( 2 ); 710 } 711 else if( Detail::startsWith( optName, "-" ) ) 712 arg.shortNames.push_back( optName.substr( 1 ) ); 713 else 714 throw std::logic_error( "option must begin with - or --. Option was: '" + optName + "'" ); 715 } 716 friend void setPositionalArg( Arg& arg, int position ) 717 { 718 arg.position = position; 719 } 720 721 722 class ArgBuilder { 723 public: 724 ArgBuilder( Arg* arg ) : m_arg( arg ) {} 725 726 // Bind a non-boolean data member (requires placeholder string) 727 template<typename C, typename M> 728 void bind( M C::* field, std::string const& placeholder ) { 729 m_arg->boundField = new Detail::BoundDataMember<C,M>( field ); 730 m_arg->placeholder = placeholder; 731 } 732 // Bind a boolean data member (no placeholder required) 733 template<typename C> 734 void bind( bool C::* field ) { 735 m_arg->boundField = new Detail::BoundDataMember<C,bool>( field ); 736 } 737 738 // Bind a method taking a single, non-boolean argument (requires a placeholder string) 739 template<typename C, typename M> 740 void bind( void (C::* unaryMethod)( M ), std::string const& placeholder ) { 741 m_arg->boundField = new Detail::BoundUnaryMethod<C,M>( unaryMethod ); 742 m_arg->placeholder = placeholder; 743 } 744 745 // Bind a method taking a single, boolean argument (no placeholder string required) 746 template<typename C> 747 void bind( void (C::* unaryMethod)( bool ) ) { 748 m_arg->boundField = new Detail::BoundUnaryMethod<C,bool>( unaryMethod ); 749 } 750 751 // Bind a method that takes no arguments (will be called if opt is present) 752 template<typename C> 753 void bind( void (C::* nullaryMethod)() ) { 754 m_arg->boundField = new Detail::BoundNullaryMethod<C>( nullaryMethod ); 755 } 756 757 // Bind a free function taking a single argument - the object to operate on (no placeholder string required) 758 template<typename C> 759 void bind( void (* unaryFunction)( C& ) ) { 760 m_arg->boundField = new Detail::BoundUnaryFunction<C>( unaryFunction ); 761 } 762 763 // Bind a free function taking a single argument - the object to operate on (requires a placeholder string) 764 template<typename C, typename T> 765 void bind( void (* binaryFunction)( C&, T ), std::string const& placeholder ) { 766 m_arg->boundField = new Detail::BoundBinaryFunction<C, T>( binaryFunction ); 767 m_arg->placeholder = placeholder; 768 } 769 770 ArgBuilder& describe( std::string const& description ) { 771 m_arg->description = description; 772 return *this; 773 } 774 ArgBuilder& detail( std::string const& detail ) { 775 m_arg->detail = detail; 776 return *this; 777 } 778 779 protected: 780 Arg* m_arg; 781 }; 782 783 class OptBuilder : public ArgBuilder { 784 public: 785 OptBuilder( Arg* arg ) : ArgBuilder( arg ) {} 786 OptBuilder( OptBuilder& other ) : ArgBuilder( other ) {} 787 788 OptBuilder& operator[]( std::string const& optName ) { 789 addOptName( *ArgBuilder::m_arg, optName ); 790 return *this; 791 } 792 }; 793 794 public: 795 796 CommandLine() 797 : m_boundProcessName( new Detail::NullBinder<ConfigT>() ), 798 m_highestSpecifiedArgPosition( 0 ), 799 m_throwOnUnrecognisedTokens( false ) 800 {} 801 CommandLine( CommandLine const& other ) 802 : m_boundProcessName( other.m_boundProcessName ), 803 m_options ( other.m_options ), 804 m_positionalArgs( other.m_positionalArgs ), 805 m_highestSpecifiedArgPosition( other.m_highestSpecifiedArgPosition ), 806 m_throwOnUnrecognisedTokens( other.m_throwOnUnrecognisedTokens ) 807 { 808 if( other.m_floatingArg.get() ) 809 m_floatingArg.reset( new Arg( *other.m_floatingArg ) ); 810 } 811 812 CommandLine& setThrowOnUnrecognisedTokens( bool shouldThrow = true ) { 813 m_throwOnUnrecognisedTokens = shouldThrow; 814 return *this; 815 } 816 817 818 OptBuilder operator[]( std::string const& optName ) { 819 m_options.push_back( Arg() ); 820 addOptName( m_options.back(), optName ); 821 OptBuilder builder( &m_options.back() ); 822 return builder; 823 } 824 825 ArgBuilder operator[]( int position ) { 826 m_positionalArgs.insert( std::make_pair( position, Arg() ) ); 827 if( position > m_highestSpecifiedArgPosition ) 828 m_highestSpecifiedArgPosition = position; 829 setPositionalArg( m_positionalArgs[position], position ); 830 ArgBuilder builder( &m_positionalArgs[position] ); 831 return builder; 832 } 833 834 // Invoke this with the _ instance 835 ArgBuilder operator[]( UnpositionalTag ) { 836 if( m_floatingArg.get() ) 837 throw std::logic_error( "Only one unpositional argument can be added" ); 838 m_floatingArg.reset( new Arg() ); 839 ArgBuilder builder( m_floatingArg.get() ); 840 return builder; 841 } 842 843 template<typename C, typename M> 844 void bindProcessName( M C::* field ) { 845 m_boundProcessName = new Detail::BoundDataMember<C,M>( field ); 846 } 847 template<typename C, typename M> 848 void bindProcessName( void (C::*_unaryMethod)( M ) ) { 849 m_boundProcessName = new Detail::BoundUnaryMethod<C,M>( _unaryMethod ); 850 } 851 852 void optUsage( std::ostream& os, std::size_t indent = 0, std::size_t width = Detail::consoleWidth ) const { 853 typename std::vector<Arg>::const_iterator itBegin = m_options.begin(), itEnd = m_options.end(), it; 854 std::size_t maxWidth = 0; 855 for( it = itBegin; it != itEnd; ++it ) 856 maxWidth = (std::max)( maxWidth, it->commands().size() ); 857 858 for( it = itBegin; it != itEnd; ++it ) { 859 Detail::Text usage( it->commands(), Detail::TextAttributes() 860 .setWidth( maxWidth+indent ) 861 .setIndent( indent ) ); 862 Detail::Text desc( it->description, Detail::TextAttributes() 863 .setWidth( width - maxWidth - 3 ) ); 864 865 for( std::size_t i = 0; i < (std::max)( usage.size(), desc.size() ); ++i ) { 866 std::string usageCol = i < usage.size() ? usage[i] : ""; 867 os << usageCol; 868 869 if( i < desc.size() && !desc[i].empty() ) 870 os << std::string( indent + 2 + maxWidth - usageCol.size(), ' ' ) 871 << desc[i]; 872 os << "\n"; 873 } 874 } 875 } 876 std::string optUsage() const { 877 std::ostringstream oss; 878 optUsage( oss ); 879 return oss.str(); 880 } 881 882 void argSynopsis( std::ostream& os ) const { 883 for( int i = 1; i <= m_highestSpecifiedArgPosition; ++i ) { 884 if( i > 1 ) 885 os << " "; 886 typename std::map<int, Arg>::const_iterator it = m_positionalArgs.find( i ); 887 if( it != m_positionalArgs.end() ) 888 os << "<" << it->second.placeholder << ">"; 889 else if( m_floatingArg.get() ) 890 os << "<" << m_floatingArg->placeholder << ">"; 891 else 892 throw std::logic_error( "non consecutive positional arguments with no floating args" ); 893 } 894 // !TBD No indication of mandatory args 895 if( m_floatingArg.get() ) { 896 if( m_highestSpecifiedArgPosition > 1 ) 897 os << " "; 898 os << "[<" << m_floatingArg->placeholder << "> ...]"; 899 } 900 } 901 std::string argSynopsis() const { 902 std::ostringstream oss; 903 argSynopsis( oss ); 904 return oss.str(); 905 } 906 907 void usage( std::ostream& os, std::string const& procName ) const { 908 validate(); 909 os << "usage:\n " << procName << " "; 910 argSynopsis( os ); 911 if( !m_options.empty() ) { 912 os << " [options]\n\nwhere options are: \n"; 913 optUsage( os, 2 ); 914 } 915 os << "\n"; 916 } 917 std::string usage( std::string const& procName ) const { 918 std::ostringstream oss; 919 usage( oss, procName ); 920 return oss.str(); 921 } 922 923 ConfigT parse( std::vector<std::string> const& args ) const { 924 ConfigT config; 925 parseInto( args, config ); 926 return config; 927 } 928 929 std::vector<Parser::Token> parseInto( std::vector<std::string> const& args, ConfigT& config ) const { 930 std::string processName = args.empty() ? std::string() : args[0]; 931 std::size_t lastSlash = processName.find_last_of( "/\\" ); 932 if( lastSlash != std::string::npos ) 933 processName = processName.substr( lastSlash+1 ); 934 m_boundProcessName.set( config, processName ); 935 std::vector<Parser::Token> tokens; 936 Parser parser; 937 parser.parseIntoTokens( args, tokens ); 938 return populate( tokens, config ); 939 } 940 941 std::vector<Parser::Token> populate( std::vector<Parser::Token> const& tokens, ConfigT& config ) const { 942 validate(); 943 std::vector<Parser::Token> unusedTokens = populateOptions( tokens, config ); 944 unusedTokens = populateFixedArgs( unusedTokens, config ); 945 unusedTokens = populateFloatingArgs( unusedTokens, config ); 946 return unusedTokens; 947 } 948 949 std::vector<Parser::Token> populateOptions( std::vector<Parser::Token> const& tokens, ConfigT& config ) const { 950 std::vector<Parser::Token> unusedTokens; 951 std::vector<std::string> errors; 952 for( std::size_t i = 0; i < tokens.size(); ++i ) { 953 Parser::Token const& token = tokens[i]; 954 typename std::vector<Arg>::const_iterator it = m_options.begin(), itEnd = m_options.end(); 955 for(; it != itEnd; ++it ) { 956 Arg const& arg = *it; 957 958 try { 959 if( ( token.type == Parser::Token::ShortOpt && arg.hasShortName( token.data ) ) || 960 ( token.type == Parser::Token::LongOpt && arg.hasLongName( token.data ) ) ) { 961 if( arg.takesArg() ) { 962 if( i == tokens.size()-1 || tokens[i+1].type != Parser::Token::Positional ) 963 errors.push_back( "Expected argument to option: " + token.data ); 964 else 965 arg.boundField.set( config, tokens[++i].data ); 966 } 967 else { 968 arg.boundField.set( config, "true" ); 969 } 970 break; 971 } 972 } 973 catch( std::exception& ex ) { 974 errors.push_back( std::string( ex.what() ) + "\n- while parsing: (" + arg.commands() + ")" ); 975 } 976 } 977 if( it == itEnd ) { 978 if( token.type == Parser::Token::Positional || !m_throwOnUnrecognisedTokens ) 979 unusedTokens.push_back( token ); 980 else if( errors.empty() && m_throwOnUnrecognisedTokens ) 981 errors.push_back( "unrecognised option: " + token.data ); 982 } 983 } 984 if( !errors.empty() ) { 985 std::ostringstream oss; 986 for( std::vector<std::string>::const_iterator it = errors.begin(), itEnd = errors.end(); 987 it != itEnd; 988 ++it ) { 989 if( it != errors.begin() ) 990 oss << "\n"; 991 oss << *it; 992 } 993 throw std::runtime_error( oss.str() ); 994 } 995 return unusedTokens; 996 } 997 std::vector<Parser::Token> populateFixedArgs( std::vector<Parser::Token> const& tokens, ConfigT& config ) const { 998 std::vector<Parser::Token> unusedTokens; 999 int position = 1; 1000 for( std::size_t i = 0; i < tokens.size(); ++i ) { 1001 Parser::Token const& token = tokens[i]; 1002 typename std::map<int, Arg>::const_iterator it = m_positionalArgs.find( position ); 1003 if( it != m_positionalArgs.end() ) 1004 it->second.boundField.set( config, token.data ); 1005 else 1006 unusedTokens.push_back( token ); 1007 if( token.type == Parser::Token::Positional ) 1008 position++; 1009 } 1010 return unusedTokens; 1011 } 1012 std::vector<Parser::Token> populateFloatingArgs( std::vector<Parser::Token> const& tokens, ConfigT& config ) const { 1013 if( !m_floatingArg.get() ) 1014 return tokens; 1015 std::vector<Parser::Token> unusedTokens; 1016 for( std::size_t i = 0; i < tokens.size(); ++i ) { 1017 Parser::Token const& token = tokens[i]; 1018 if( token.type == Parser::Token::Positional ) 1019 m_floatingArg->boundField.set( config, token.data ); 1020 else 1021 unusedTokens.push_back( token ); 1022 } 1023 return unusedTokens; 1024 } 1025 1026 void validate() const 1027 { 1028 if( m_options.empty() && m_positionalArgs.empty() && !m_floatingArg.get() ) 1029 throw std::logic_error( "No options or arguments specified" ); 1030 1031 for( typename std::vector<Arg>::const_iterator it = m_options.begin(), 1032 itEnd = m_options.end(); 1033 it != itEnd; ++it ) 1034 it->validate(); 1035 } 1036 1037 private: 1038 Detail::BoundArgFunction<ConfigT> m_boundProcessName; 1039 std::vector<Arg> m_options; 1040 std::map<int, Arg> m_positionalArgs; 1041 ArgAutoPtr m_floatingArg; 1042 int m_highestSpecifiedArgPosition; 1043 bool m_throwOnUnrecognisedTokens; 1044 }; 1045 1046 } // end namespace Clara 1047 1048 1049 STITCH_CLARA_CLOSE_NAMESPACE 1050 #undef STITCH_CLARA_OPEN_NAMESPACE 1051 #undef STITCH_CLARA_CLOSE_NAMESPACE 1052 1053 1054 #endif // TWOBLUECUBES_CLARA_H_INCLUDED 1055