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