| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  *  Created by Phil on 22/10/2010. | 
					
						
							|  |  |  |  *  Copyright 2010 Two Blue Cubes Ltd | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  Distributed under the Boost Software License, Version 1.0. (See accompanying | 
					
						
							|  |  |  |  *  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #ifdef __clang__
 | 
					
						
							|  |  |  | #pragma clang diagnostic ignored "-Wpadded"
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "catch.hpp"
 | 
					
						
							| 
									
										
										
										
											2013-05-01 19:01:48 +01:00
										 |  |  | #include "catch_text.h"
 | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-01 19:01:48 +01:00
										 |  |  | namespace Clara { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  | template<typename T> struct RemoveConstRef{ typedef T type; }; | 
					
						
							|  |  |  | template<typename T> struct RemoveConstRef<T&>{ typedef T type; }; | 
					
						
							|  |  |  | template<typename T> struct RemoveConstRef<T const&>{ typedef T type; }; | 
					
						
							|  |  |  | template<typename T> struct RemoveConstRef<T const>{ typedef T type; }; | 
					
						
							| 
									
										
										
										
											2013-05-01 19:01:48 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  | using namespace Catch; | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-29 19:26:18 +01:00
										 |  |  | template<typename T> | 
					
						
							|  |  |  | bool convertInto( std::string const& _source, T& _dest ) { | 
					
						
							|  |  |  |     std::stringstream ss; | 
					
						
							|  |  |  |     ss << _source; | 
					
						
							|  |  |  |     ss >> _dest; | 
					
						
							|  |  |  |     return !ss.fail(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | inline bool convertInto( std::string const& _source, std::string& _dest ) { | 
					
						
							|  |  |  |     _dest = _source; | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  | inline bool convertInto( std::string const& _source, bool _dest ) { | 
					
						
							|  |  |  |     std::string sourceLC = toLower( _source ); | 
					
						
							|  |  |  |     if( sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on" ) | 
					
						
							|  |  |  |         _dest = true; | 
					
						
							|  |  |  |     else if( sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off" ) | 
					
						
							|  |  |  |         _dest = false; | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2013-04-29 19:26:18 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  | template<typename T> | 
					
						
							|  |  |  | class Opt { | 
					
						
							|  |  |  | public: | 
					
						
							|  |  |  |     Opt( std::string const& _synposis ) : m_synopsis( _synposis ) {} | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |     Opt& shortName( std::string const& _value ) { m_shortName = _value; return *this; } | 
					
						
							|  |  |  |     Opt& longName( std::string const& _value )  { m_longName = _value; return *this; } | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     template<typename M> | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |     Opt& optArg( std::string const& _name, M const& _member  ){ | 
					
						
							|  |  |  |         m_argName = _name; | 
					
						
							|  |  |  |         m_field = new Field<M>( _member ); | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  |         return *this; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |     template<typename M> | 
					
						
							|  |  |  |     Opt& flag( M const& _member  ){ | 
					
						
							|  |  |  |         m_field = new FlagField<M>( _member ); | 
					
						
							|  |  |  |         return *this; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     std::size_t takesArg() const { return !m_argName.empty(); } | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  |     std::string synopsis() const { return m_synopsis; } | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |     std::string shortName() const { return m_shortName; } | 
					
						
							|  |  |  |     std::string longName() const { return m_longName; } | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  |      | 
					
						
							|  |  |  |     bool parseInto( std::string const& _arg, T& _config ) const { | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |         if( m_argName.empty() ) | 
					
						
							|  |  |  |             m_field->set( _config ); | 
					
						
							|  |  |  |         else if( !m_field->parseInto( _arg, _config ) ) | 
					
						
							|  |  |  |             throw std::domain_error( "'" + _arg + "' was not valid for <" + m_argName + ">" );                 | 
					
						
							|  |  |  |         return true; | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2013-05-01 19:01:48 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     std::string usage() const { | 
					
						
							|  |  |  |         std::ostringstream oss; | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |         writeToStream( oss ); | 
					
						
							| 
									
										
										
										
											2013-05-01 19:01:48 +01:00
										 |  |  |         return oss.str(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     friend std::ostream& operator << ( std::ostream& os, Opt const& _opt ) { | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |         _opt.writeToStream( os ); | 
					
						
							| 
									
										
										
										
											2013-05-01 19:01:48 +01:00
										 |  |  |         return os; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |     void writeToStream ( std::ostream& os ) const { | 
					
						
							|  |  |  |         if( !m_shortName.empty() ) | 
					
						
							|  |  |  |             os << "-" << m_shortName; | 
					
						
							|  |  |  |             if( !m_longName.empty() ) | 
					
						
							|  |  |  |                 os << ", "; | 
					
						
							|  |  |  |         if( !m_longName.empty() ) | 
					
						
							|  |  |  |             os << "--" << m_longName; | 
					
						
							|  |  |  |         if( takesArg() ) | 
					
						
							|  |  |  |             os << " <" << m_argName << ">"; | 
					
						
							|  |  |  |     }     | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  | private: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     struct IField : SharedImpl<> { | 
					
						
							|  |  |  |         virtual ~IField() {} | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |         virtual bool parseInto( std::string const&, T& ) const { | 
					
						
							|  |  |  |             throw std::logic_error( "cannot pass argument to bool binder" ); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         virtual void set( T& ) const { | 
					
						
							|  |  |  |             throw std::logic_error( "field requires an argument" ); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  |     }; | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |          | 
					
						
							| 
									
										
										
										
											2013-05-01 19:01:48 +01:00
										 |  |  |     template<typename F> | 
					
						
							|  |  |  |     struct Field; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |     template<typename F> | 
					
						
							|  |  |  |     struct FlagField; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Data member : with argument
 | 
					
						
							| 
									
										
										
										
											2013-05-01 19:01:48 +01:00
										 |  |  |     template<typename C, typename M> | 
					
						
							|  |  |  |     struct Field<M C::*> : IField { | 
					
						
							|  |  |  |         Field( M C::* _member ) : member( _member ) {} | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  |         bool parseInto( std::string const& _arg, T& _config ) const { | 
					
						
							| 
									
										
										
										
											2013-04-29 19:26:18 +01:00
										 |  |  |             return convertInto( _arg, _config.*member ); | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2013-05-01 19:01:48 +01:00
										 |  |  |         M C::* member; | 
					
						
							|  |  |  |     }; | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |     // Data member : flag
 | 
					
						
							|  |  |  |     template<typename C, typename M> | 
					
						
							|  |  |  |     struct FlagField<M C::*> : IField { | 
					
						
							|  |  |  |         FlagField( M C::* _member ) : member( _member ) {} | 
					
						
							|  |  |  |         void set( T& _config ) const { | 
					
						
							|  |  |  |             _config.*member = true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         M C::* member; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     // Unary method : with argument
 | 
					
						
							| 
									
										
										
										
											2013-05-01 19:01:48 +01:00
										 |  |  |     template<typename C, typename M> | 
					
						
							|  |  |  |     struct Field<void (C::*)( M )> : IField { | 
					
						
							|  |  |  |         Field( void (C::*_method)( M ) ) : method( _method ) {} | 
					
						
							|  |  |  |         bool parseInto( std::string const& _arg, T& _config ) const { | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |             typename RemoveConstRef<M>::type value; | 
					
						
							| 
									
										
										
										
											2013-05-01 19:01:48 +01:00
										 |  |  |             if( !convertInto( _arg, value ) ) | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |             ( _config.*method )( value ); | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         void (C::*method)( M ); | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  |     }; | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Unary method : flag
 | 
					
						
							|  |  |  |     template<typename C, typename M> | 
					
						
							|  |  |  |     struct FlagField<void (C::*)( M )> : IField { | 
					
						
							|  |  |  |         FlagField( void (C::*_method)( M ) ) : method( _method ) {} | 
					
						
							|  |  |  |         void set( T& _config ) const { | 
					
						
							|  |  |  |             ( _config.*method )( true ); | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |         void (C::*method)( M ); | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  |     }; | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Nullary method : flag
 | 
					
						
							|  |  |  |     template<typename C> | 
					
						
							|  |  |  |     struct FlagField<void (C::*)()> : IField { | 
					
						
							|  |  |  |         FlagField( void (C::*_method)() ) : method( _method ) {} | 
					
						
							|  |  |  |         void set( T& _config ) const { | 
					
						
							|  |  |  |             ( _config.*method )(); | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |         void (C::*method)(); | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |      | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  |     std::string m_synopsis; | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |     std::string m_shortName; | 
					
						
							|  |  |  |     std::string m_longName; | 
					
						
							|  |  |  |     std::string m_argName; | 
					
						
							|  |  |  |     Ptr<IField> m_field; | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | template<typename T> | 
					
						
							| 
									
										
										
										
											2013-05-02 18:41:08 +01:00
										 |  |  | class Parser | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  | { | 
					
						
							|  |  |  | public: | 
					
						
							| 
									
										
										
										
											2013-05-03 08:08:46 +01:00
										 |  |  |     Parser() | 
					
						
							|  |  |  |     :   m_separatorChars( "=: " ), | 
					
						
							|  |  |  |         m_allowSpaceSeparator( m_separatorChars.find( ' ' ) != std::string::npos ) | 
					
						
							|  |  |  |     {} | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |     template<typename M> | 
					
						
							|  |  |  |     Parser( std::string const&, M ) // !TBD
 | 
					
						
							|  |  |  |     :   m_separatorChars( "=: " ), | 
					
						
							|  |  |  |         m_allowSpaceSeparator( m_separatorChars.find( ' ' ) != std::string::npos ) | 
					
						
							|  |  |  |     {} | 
					
						
							|  |  |  |     template<typename M> | 
					
						
							|  |  |  |     Parser& operator()( std::string const&, M ) { return *this; } // !TBD
 | 
					
						
							| 
									
										
										
										
											2013-05-03 08:08:46 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-02 18:41:08 +01:00
										 |  |  |     Opt<T>& addOption( std::string const& _synposis ) { | 
					
						
							|  |  |  |         m_allOptionParsers.push_back( _synposis ); | 
					
						
							|  |  |  |         return m_allOptionParsers.back(); | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2013-04-29 19:26:18 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |     void parseInto( int argc, const char* const argv[], T& _config ) { | 
					
						
							| 
									
										
										
										
											2013-05-03 08:08:46 +01:00
										 |  |  |         std::vector<std::string> args; | 
					
						
							|  |  |  |         args.reserve( static_cast<std::size_t>( argc ) ); | 
					
						
							| 
									
										
										
										
											2013-04-29 19:26:18 +01:00
										 |  |  |         for( int i = 0; i < argc; ++i ) | 
					
						
							| 
									
										
										
										
											2013-05-03 08:08:46 +01:00
										 |  |  |             args.push_back( argv[i] ); | 
					
						
							|  |  |  |          | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |         parseInto( args, _config ); | 
					
						
							| 
									
										
										
										
											2013-04-29 19:26:18 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     template<typename U> | 
					
						
							| 
									
										
										
										
											2013-05-02 18:41:08 +01:00
										 |  |  |     void parseRemainingArgs( Parser<U>& _parser, T& _config ) { | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |         parseInto( _parser.m_unusedOpts, _config ); | 
					
						
							| 
									
										
										
										
											2013-04-29 19:26:18 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |     void parseInto( std::vector<std::string> const& _args, T& _config ) { | 
					
						
							| 
									
										
										
										
											2013-05-02 18:41:08 +01:00
										 |  |  |         ensureOptions(); | 
					
						
							| 
									
										
										
										
											2013-05-03 08:08:46 +01:00
										 |  |  |         for( std::size_t i = 0; i < _args.size(); ++i ) { | 
					
						
							|  |  |  |             std::string const& arg = _args[i]; | 
					
						
							|  |  |  |             if( arg[0] == '-' ) { | 
					
						
							|  |  |  |                 std::string optArgs, optName; | 
					
						
							|  |  |  |                 std::size_t pos = arg.find_first_of( m_separatorChars ); | 
					
						
							|  |  |  |                 if( pos == std::string::npos ) { | 
					
						
							|  |  |  |                     optName = arg; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 else { | 
					
						
							|  |  |  |                     optName = arg.substr(0, pos ); | 
					
						
							|  |  |  |                     optArgs = arg.substr( pos+1 ); | 
					
						
							| 
									
										
										
										
											2013-04-29 19:26:18 +01:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2013-05-03 08:08:46 +01:00
										 |  |  |                 typename std::map<std::string, Opt<T> const*>::const_iterator it = m_optionsByName.find( optName ); | 
					
						
							|  |  |  |                 bool used = false; | 
					
						
							|  |  |  |                 if( it != m_optionsByName.end() ) { | 
					
						
							|  |  |  |                     Opt<T> const& opt = *(it->second);                     | 
					
						
							|  |  |  |                     if( opt.takesArg() ) { | 
					
						
							|  |  |  |                         if( optArgs.empty() ) { | 
					
						
							|  |  |  |                             if( i < _args.size() && _args[i+1][0] != '-' ) | 
					
						
							|  |  |  |                                 optArgs = _args[++i]; | 
					
						
							|  |  |  |                             else | 
					
						
							|  |  |  |                                 throw std::domain_error( "Expected argument"); // !TBD better error
 | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                     try { | 
					
						
							|  |  |  |                         used = opt.parseInto( optArgs, _config ); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                     catch( std::exception& ex ) { | 
					
						
							|  |  |  |                         throw std::domain_error( "Error in " + optName + " option: " + ex.what() ); | 
					
						
							|  |  |  |                     } | 
					
						
							| 
									
										
										
										
											2013-04-29 19:26:18 +01:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2013-05-03 08:08:46 +01:00
										 |  |  |                 if( !used ) | 
					
						
							|  |  |  |                     m_unusedOpts.push_back( arg ); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             else { | 
					
						
							|  |  |  |                 m_args.push_back( arg ); | 
					
						
							| 
									
										
										
										
											2013-04-29 19:26:18 +01:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2013-04-29 19:26:18 +01:00
										 |  |  |      | 
					
						
							| 
									
										
										
										
											2013-05-02 18:41:08 +01:00
										 |  |  |     friend std::ostream& operator <<( std::ostream& os, Parser const& _parser ) { | 
					
						
							| 
									
										
										
										
											2013-05-01 19:01:48 +01:00
										 |  |  |         typename std::vector<Opt<T> >::const_iterator it, itEnd = _parser.m_allOptionParsers.end(); | 
					
						
							|  |  |  |         std::size_t maxWidth = 0; | 
					
						
							|  |  |  |         for(it = _parser.m_allOptionParsers.begin(); it != itEnd; ++it ) | 
					
						
							|  |  |  |             maxWidth = (std::max)( it->usage().size(), maxWidth ); | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         for(it = _parser.m_allOptionParsers.begin(); it != itEnd; ++it ) { | 
					
						
							|  |  |  |             Text usage( it->usage(), TextAttributes().setWidth( maxWidth ) ); | 
					
						
							|  |  |  |             // !TBD handle longer usage strings
 | 
					
						
							|  |  |  |             Text synopsis( it->synopsis(), TextAttributes().setWidth( CATCH_CONFIG_CONSOLE_WIDTH - maxWidth -3 ) ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             for( std::size_t i = 0; i < std::max( usage.size(), synopsis.size() ); ++i ) { | 
					
						
							|  |  |  |                 std::string usageCol = i < usage.size() ? usage[i] : ""; | 
					
						
							|  |  |  |                 std::cout << usageCol; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if( i < synopsis.size() && !synopsis[i].empty() ) | 
					
						
							|  |  |  |                     std::cout   << std::string( 2 + maxWidth - usageCol.size(), ' ' ) | 
					
						
							|  |  |  |                                 << synopsis[i]; | 
					
						
							|  |  |  |                 std::cout << "\n"; | 
					
						
							|  |  |  |             }             | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return os; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  | private: | 
					
						
							| 
									
										
										
										
											2013-05-02 18:41:08 +01:00
										 |  |  |     void ensureOptions() const { | 
					
						
							|  |  |  |         if( m_allOptionParsers.size() != m_optionsByName.size() ) { | 
					
						
							|  |  |  |             m_optionsByName.clear(); | 
					
						
							|  |  |  |             typename std::vector<Opt<T> >::const_iterator it, itEnd = m_allOptionParsers.end(); | 
					
						
							|  |  |  |             for( it = m_allOptionParsers.begin(); it != itEnd; ++it ) { | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |                 if( !it->shortName().empty() ) | 
					
						
							|  |  |  |                     m_optionsByName.insert( std::make_pair( "-" + it->shortName(), &*it ) ); | 
					
						
							|  |  |  |                 if( !it->longName().empty() ) | 
					
						
							|  |  |  |                     m_optionsByName.insert( std::make_pair( "--" + it->longName(), &*it ) );                 | 
					
						
							| 
									
										
										
										
											2013-05-02 18:41:08 +01:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2013-04-29 19:26:18 +01:00
										 |  |  |     template<typename U> | 
					
						
							| 
									
										
										
										
											2013-05-02 18:41:08 +01:00
										 |  |  |     friend class Parser; | 
					
						
							| 
									
										
										
										
											2013-04-29 19:26:18 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  |     std::vector<Opt<T> > m_allOptionParsers; | 
					
						
							| 
									
										
										
										
											2013-05-02 18:41:08 +01:00
										 |  |  |     mutable std::map<std::string, Opt<T> const*> m_optionsByName; | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  |     std::vector<std::string> m_args; | 
					
						
							|  |  |  |     std::vector<std::string> m_unusedOpts; | 
					
						
							| 
									
										
										
										
											2013-05-03 08:08:46 +01:00
										 |  |  |     std::string m_separatorChars; | 
					
						
							|  |  |  |     bool m_allowSpaceSeparator; | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  | } // end namespace Catch
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct TestOpt { | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |     TestOpt() : number( 0 ), index( 0 ), flag( false ) {} | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     std::string fileName; | 
					
						
							|  |  |  |     int number; | 
					
						
							| 
									
										
										
										
											2013-05-01 19:01:48 +01:00
										 |  |  |     int index; | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |     bool flag; | 
					
						
							| 
									
										
										
										
											2013-05-01 19:01:48 +01:00
										 |  |  |      | 
					
						
							|  |  |  |     void setValidIndex( int i ) { | 
					
						
							|  |  |  |         if( i < 0 || i > 10 ) | 
					
						
							|  |  |  |             throw std::domain_error( "index must be between 0 and 10" ); | 
					
						
							|  |  |  |         index = i; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-29 19:26:18 +01:00
										 |  |  | struct TestOpt2 { | 
					
						
							|  |  |  |     std::string description; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-14 19:31:21 +01:00
										 |  |  | #ifdef CATCH_CONFIG_VARIADIC_MACROS
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  | TEST_CASE( "cmdline", "" ) { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-02 18:41:08 +01:00
										 |  |  |     TestOpt config; | 
					
						
							|  |  |  |     Clara::Parser<TestOpt> parser; | 
					
						
							|  |  |  |     parser.addOption( "specifies output file" ) | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |         .shortName( "o" ) | 
					
						
							|  |  |  |         .longName( "output" ) | 
					
						
							|  |  |  |         .optArg( "<filename>", &TestOpt::fileName ); | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     SECTION( "plain filename" ) { | 
					
						
							| 
									
										
										
										
											2013-05-03 08:08:46 +01:00
										 |  |  |         const char* argv[] = { "test", "-o filename.ext" }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |         parser.parseInto( sizeof(argv)/sizeof(char*), argv, config ); | 
					
						
							| 
									
										
										
										
											2013-05-03 08:08:46 +01:00
										 |  |  |         CHECK( config.fileName == "filename.ext" ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     SECTION( "plain filename with colon" ) { | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  |         const char* argv[] = { "test", "-o:filename.ext" }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |         parser.parseInto( sizeof(argv)/sizeof(char*), argv, config ); | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  |         CHECK( config.fileName == "filename.ext" ); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2013-05-03 08:08:46 +01:00
										 |  |  |     SECTION( "plain filename with =" ) { | 
					
						
							|  |  |  |         const char* argv[] = { "test", "-o=filename.ext" }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |         parser.parseInto( sizeof(argv)/sizeof(char*), argv, config ); | 
					
						
							| 
									
										
										
										
											2013-05-03 08:08:46 +01:00
										 |  |  |         CHECK( config.fileName == "filename.ext" ); | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  |     } | 
					
						
							|  |  |  |     SECTION( "long opt" ) { | 
					
						
							| 
									
										
										
										
											2013-05-03 08:08:46 +01:00
										 |  |  |         const char* argv[] = { "test", "--output %stdout" }; | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |         parser.parseInto( sizeof(argv)/sizeof(char*), argv, config ); | 
					
						
							|  |  |  |         CHECK( config.fileName == "%stdout" ); | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							| 
									
										
										
										
											2013-05-02 18:41:08 +01:00
										 |  |  |     parser.addOption( "a number" ) | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |             .shortName( "n" ) | 
					
						
							|  |  |  |             .optArg( "<an integral value>", &TestOpt::number ); | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  |      | 
					
						
							|  |  |  |     SECTION( "a number" ) { | 
					
						
							| 
									
										
										
										
											2013-05-03 08:08:46 +01:00
										 |  |  |         const char* argv[] = { "test", "-n 42" }; | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |         parser.parseInto( sizeof(argv)/sizeof(char*), argv, config ); | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  |         CHECK( config.number == 42 ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     SECTION( "not a number" ) { | 
					
						
							| 
									
										
										
										
											2013-05-03 08:08:46 +01:00
										 |  |  |         const char* argv[] = { "test", "-n forty-two" }; | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |         CHECK_THROWS( parser.parseInto( sizeof(argv)/sizeof(char*), argv, config ) ); | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  |         CHECK( config.number == 0 ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							| 
									
										
										
										
											2013-04-29 19:26:18 +01:00
										 |  |  |     SECTION( "two parsers" ) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         TestOpt config1; | 
					
						
							|  |  |  |         TestOpt2 config2; | 
					
						
							| 
									
										
										
										
											2013-05-02 18:41:08 +01:00
										 |  |  |         Clara::Parser<TestOpt2> parser2; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         parser2.addOption( "description" ) | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |                     .shortName( "d" ) | 
					
						
							|  |  |  |                     .longName( "description" ) | 
					
						
							|  |  |  |                     .optArg( "<some text>", &TestOpt2::description ); | 
					
						
							| 
									
										
										
										
											2013-04-29 19:26:18 +01:00
										 |  |  |          | 
					
						
							| 
									
										
										
										
											2013-05-03 08:08:46 +01:00
										 |  |  |         const char* argv[] = { "test", "-n 42", "-d some text" }; | 
					
						
							| 
									
										
										
										
											2013-04-29 19:26:18 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |         parser.parseInto( sizeof(argv)/sizeof(char*), argv, config1 ); | 
					
						
							| 
									
										
										
										
											2013-04-29 19:26:18 +01:00
										 |  |  |         CHECK( config1.number == 42 ); | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         parser2.parseRemainingArgs( parser, config2 ); | 
					
						
							|  |  |  |         CHECK( config2.description == "some text" ); | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2013-05-01 19:01:48 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     SECTION( "methods" ) { | 
					
						
							| 
									
										
										
										
											2013-05-02 18:41:08 +01:00
										 |  |  |         parser.addOption( "An index, which is an integer between 0 and 10, inclusive" ) | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |                 .shortName( "i" ) | 
					
						
							|  |  |  |                 .optArg( "<index>", &TestOpt::setValidIndex ); | 
					
						
							| 
									
										
										
										
											2013-05-01 19:01:48 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         SECTION( "in range" ) { | 
					
						
							| 
									
										
										
										
											2013-05-03 08:08:46 +01:00
										 |  |  |             const char* argv[] = { "test", "-i 3" }; | 
					
						
							| 
									
										
										
										
											2013-05-01 19:01:48 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |             parser.parseInto( sizeof(argv)/sizeof(char*), argv, config ); | 
					
						
							| 
									
										
										
										
											2013-05-01 19:01:48 +01:00
										 |  |  |             REQUIRE( config.index == 3 ); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         SECTION( "out of range" ) { | 
					
						
							| 
									
										
										
										
											2013-05-03 08:08:46 +01:00
										 |  |  |             const char* argv[] = { "test", "-i 42" }; | 
					
						
							| 
									
										
										
										
											2013-05-01 19:01:48 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |             REQUIRE_THROWS( parser.parseInto( sizeof(argv)/sizeof(char*), argv, config ) ); | 
					
						
							| 
									
										
										
										
											2013-05-01 19:01:48 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     SECTION( "flags" ) { | 
					
						
							|  |  |  |         parser.addOption( "A flag" ) | 
					
						
							|  |  |  |                 .shortName( "f" ) | 
					
						
							|  |  |  |                 .flag( &TestOpt::flag ); | 
					
						
							| 
									
										
										
										
											2013-05-01 19:01:48 +01:00
										 |  |  |          | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |         SECTION( "set" ) { | 
					
						
							|  |  |  |             const char* argv[] = { "test", "-f" }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             parser.parseInto( sizeof(argv)/sizeof(char*), argv, config ); | 
					
						
							|  |  |  |             REQUIRE( config.flag ); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         SECTION( "not set" ) { | 
					
						
							|  |  |  |             const char* argv[] = { "test" }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             parser.parseInto( sizeof(argv)/sizeof(char*), argv, config ); | 
					
						
							|  |  |  |             REQUIRE( config.flag == false ); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2013-05-01 19:01:48 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct Config { | 
					
						
							|  |  |  |     bool listTests; | 
					
						
							|  |  |  |     bool listTags; | 
					
						
							|  |  |  |     bool showPassingTests; | 
					
						
							|  |  |  |     bool breakIntoDebugger; | 
					
						
							|  |  |  |     bool noThrow; | 
					
						
							|  |  |  |     bool showHelp; | 
					
						
							| 
									
										
										
										
											2013-04-29 19:26:18 +01:00
										 |  |  |      | 
					
						
							| 
									
										
										
										
											2013-05-07 18:53:14 +01:00
										 |  |  |     int abortAfter; | 
					
						
							|  |  |  |     std::string reporterName; | 
					
						
							|  |  |  |     std::string fileName; | 
					
						
							|  |  |  |     std::string suiteName; | 
					
						
							|  |  |  |     std::vector<std::string> warnings; | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     void abortAfterFirst() { abortAfter = 1; } | 
					
						
							|  |  |  |     void abortAfterX( int x ) { abortAfter = x; } | 
					
						
							|  |  |  |     void addWarning( std::string const& _warning ) { warnings.push_back( _warning ); } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | TEST_CASE( "growing new Catch cli" ) { | 
					
						
							|  |  |  |     Clara::Parser<Config> parser; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Clara::Parser<Config> | 
					
						
							|  |  |  |         ( "-h, --help                         display usage information",           &Config::showHelp ) | 
					
						
							|  |  |  |         ( "-l, --list                         list all (or matching) test cases",   &Config::listTests ) | 
					
						
							|  |  |  |         ( "-t, --tags                         list all (or matching) tags",         &Config::listTags ) | 
					
						
							|  |  |  |         ( "-p, --passing                      show passing test output",            &Config::showPassingTests ) | 
					
						
							|  |  |  |         ( "-b, --break                        break into debugger on failure",      &Config::breakIntoDebugger ) | 
					
						
							|  |  |  |         ( "-e, --nothrow                      Skip exception tests",                &Config::noThrow ) | 
					
						
							|  |  |  |         ( "-o, --out <file name>              output filename",                     &Config::fileName ) | 
					
						
							|  |  |  |         ( "-n, --name <name>                  suite name",                          &Config::suiteName ) | 
					
						
							|  |  |  |         ( "-a, --abort                        abort at first failure",              &Config::abortAfterFirst ) | 
					
						
							|  |  |  |         ( "-x, --abortx <number of failures>  abort after x failures",              &Config::abortAfterX ) | 
					
						
							|  |  |  |         ( "-w, --warn <warning name>          enables warnings",                    &Config::addWarning ); | 
					
						
							|  |  |  | //        .parseInto( argc, argv, config );
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     parser.addOption( "display usage information" ) | 
					
						
							|  |  |  |         .shortName( "?") | 
					
						
							|  |  |  |         .shortName( "h") | 
					
						
							|  |  |  |         .longName( "help" ) | 
					
						
							|  |  |  |         .flag( &Config::showHelp ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     parser.addOption( "list all (or matching) test cases" ) | 
					
						
							|  |  |  |         .shortName( "l") | 
					
						
							|  |  |  |         .longName( "list" ) | 
					
						
							|  |  |  |         .flag( &Config::listTests ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     parser.addOption( "list all (or matching) tags" ) | 
					
						
							|  |  |  |         .shortName( "t") | 
					
						
							|  |  |  |         .longName( "tags" ) | 
					
						
							|  |  |  |         .flag( &Config::listTags ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     parser.addOption( "show passing test output" ) | 
					
						
							|  |  |  |         .shortName( "p") | 
					
						
							|  |  |  |         .longName( "passing" ) | 
					
						
							|  |  |  |         .flag( &Config::showPassingTests ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     parser.addOption( "break into debugger on failure" ) | 
					
						
							|  |  |  |         .shortName( "b") | 
					
						
							|  |  |  |         .longName( "break" ) | 
					
						
							|  |  |  |         .flag( &Config::breakIntoDebugger ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     parser.addOption( "Skip exception tests" ) | 
					
						
							|  |  |  |         .shortName( "e") | 
					
						
							|  |  |  |         .longName( "nothrow" ) | 
					
						
							|  |  |  |         .flag( &Config::noThrow ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     parser.addOption( "output filename" ) | 
					
						
							|  |  |  |         .shortName( "o") | 
					
						
							|  |  |  |         .longName( "out" ) | 
					
						
							|  |  |  |         .optArg( "<file name>", &Config::fileName ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     parser.addOption( "suite name" ) | 
					
						
							|  |  |  |         .shortName( "n") | 
					
						
							|  |  |  |         .longName( "name" ) | 
					
						
							|  |  |  |         .optArg( "<name>", &Config::suiteName ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     parser.addOption( "abort at first failure" ) | 
					
						
							|  |  |  |         .shortName( "a") | 
					
						
							|  |  |  |         .longName( "abort" ) | 
					
						
							|  |  |  |         .flag( &Config::abortAfterFirst ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     parser.addOption( "abort after x failures" ) | 
					
						
							|  |  |  |         .shortName( "x") | 
					
						
							|  |  |  |         .longName( "abortx" ) | 
					
						
							|  |  |  |         .optArg( "<number of failures>", &Config::abortAfterX ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     parser.addOption( "enables warnings" ) | 
					
						
							|  |  |  |         .shortName( "w") | 
					
						
							|  |  |  |         .longName( "warn" ) | 
					
						
							|  |  |  |         .optArg( "<warning name>", &Config::addWarning ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     std::cout << parser << std::endl; | 
					
						
							| 
									
										
										
										
											2013-04-27 12:26:13 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2013-05-14 19:31:21 +01:00
										 |  |  | #endif
 | 
					
						
							|  |  |  | 
 |