Updated embedded Clara to 0.0.2.3

- has all new, more robust, token parsing.
- eliminates issue with unreachable code
- allows use of forward slashes to introduce short args on Windows
This commit is contained in:
Phil Nash 2016-04-23 13:21:29 +01:00
parent 6f3bc629be
commit 1c47fe023a
3 changed files with 96 additions and 79 deletions

View File

@ -131,10 +131,10 @@ namespace Catch {
Catch::cout() << "For more detail usage please see the project docs\n" << std::endl; Catch::cout() << "For more detail usage please see the project docs\n" << std::endl;
} }
int applyCommandLine( int argc, char const* argv[], OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) { int applyCommandLine( int argc, char const* const* const argv, OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) {
try { try {
m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail ); m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail );
m_unusedTokens = m_cli.parseInto( argc, argv, m_configData ); m_unusedTokens = m_cli.parseInto( Clara::argsToVector( argc, argv ), m_configData );
if( m_configData.showHelp ) if( m_configData.showHelp )
showHelp( m_configData.processName ); showHelp( m_configData.processName );
m_config.reset(); m_config.reset();
@ -158,16 +158,13 @@ namespace Catch {
m_config.reset(); m_config.reset();
} }
int run( int argc, char const* argv[] ) { int run( int argc, char const* const* const argv ) {
int returnCode = applyCommandLine( argc, argv ); int returnCode = applyCommandLine( argc, argv );
if( returnCode == 0 ) if( returnCode == 0 )
returnCode = run(); returnCode = run();
return returnCode; return returnCode;
} }
int run( int argc, char* argv[] ) {
return run( argc, const_cast<char const**>( argv ) );
}
int run() { int run() {
if( m_configData.showHelp ) if( m_configData.showHelp )

View File

@ -6,7 +6,7 @@
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*/ */
// Version 0.0.1.1 // Version 0.0.2.3
// Only use header guard if we are not using an outer namespace // Only use header guard if we are not using an outer namespace
#if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE) #if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE)
@ -343,6 +343,11 @@ namespace Tbc {
#include <stdexcept> #include <stdexcept>
#include <memory> #include <memory>
#if defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER)
#define CLARA_PLATFORM_WINDOWS
#endif
// Use optional outer namespace // Use optional outer namespace
#ifdef STITCH_CLARA_OPEN_NAMESPACE #ifdef STITCH_CLARA_OPEN_NAMESPACE
STITCH_CLARA_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE
@ -366,9 +371,6 @@ namespace Clara {
const unsigned int consoleWidth = 80; const unsigned int consoleWidth = 80;
#endif #endif
// Use this to try and stop compiler from warning about unreachable code
inline bool isTrue( bool value ) { return value; }
using namespace Tbc; using namespace Tbc;
inline bool startsWith( std::string const& str, std::string const& prefix ) { inline bool startsWith( std::string const& str, std::string const& prefix ) {
@ -404,14 +406,7 @@ namespace Clara {
else else
throw std::runtime_error( "Expected a boolean value but did not recognise:\n '" + _source + "'" ); throw std::runtime_error( "Expected a boolean value but did not recognise:\n '" + _source + "'" );
} }
inline void convertInto( bool _source, bool& _dest ) {
_dest = _source;
}
template<typename T>
inline void convertInto( bool, T& ) {
if( isTrue( true ) )
throw std::runtime_error( "Invalid conversion" );
}
template<typename ConfigT> template<typename ConfigT>
struct IArgFunction { struct IArgFunction {
@ -421,7 +416,6 @@ namespace Clara {
IArgFunction( IArgFunction const& ) = default; IArgFunction( IArgFunction const& ) = default;
#endif #endif
virtual void set( ConfigT& config, std::string const& value ) const = 0; virtual void set( ConfigT& config, std::string const& value ) const = 0;
virtual void setFlag( ConfigT& config ) const = 0;
virtual bool takesArg() const = 0; virtual bool takesArg() const = 0;
virtual IArgFunction* clone() const = 0; virtual IArgFunction* clone() const = 0;
}; };
@ -443,9 +437,6 @@ namespace Clara {
void set( ConfigT& config, std::string const& value ) const { void set( ConfigT& config, std::string const& value ) const {
functionObj->set( config, value ); functionObj->set( config, value );
} }
void setFlag( ConfigT& config ) const {
functionObj->setFlag( config );
}
bool takesArg() const { return functionObj->takesArg(); } bool takesArg() const { return functionObj->takesArg(); }
bool isSet() const { bool isSet() const {
@ -459,7 +450,6 @@ namespace Clara {
template<typename C> template<typename C>
struct NullBinder : IArgFunction<C>{ struct NullBinder : IArgFunction<C>{
virtual void set( C&, std::string const& ) const {} virtual void set( C&, std::string const& ) const {}
virtual void setFlag( C& ) const {}
virtual bool takesArg() const { return true; } virtual bool takesArg() const { return true; }
virtual IArgFunction<C>* clone() const { return new NullBinder( *this ); } virtual IArgFunction<C>* clone() const { return new NullBinder( *this ); }
}; };
@ -470,9 +460,6 @@ namespace Clara {
virtual void set( C& p, std::string const& stringValue ) const { virtual void set( C& p, std::string const& stringValue ) const {
convertInto( stringValue, p.*member ); convertInto( stringValue, p.*member );
} }
virtual void setFlag( C& p ) const {
convertInto( true, p.*member );
}
virtual bool takesArg() const { return !IsBool<M>::value; } virtual bool takesArg() const { return !IsBool<M>::value; }
virtual IArgFunction<C>* clone() const { return new BoundDataMember( *this ); } virtual IArgFunction<C>* clone() const { return new BoundDataMember( *this ); }
M C::* member; M C::* member;
@ -485,11 +472,6 @@ namespace Clara {
convertInto( stringValue, value ); convertInto( stringValue, value );
(p.*member)( value ); (p.*member)( value );
} }
virtual void setFlag( C& p ) const {
typename RemoveConstRef<M>::type value;
convertInto( true, value );
(p.*member)( value );
}
virtual bool takesArg() const { return !IsBool<M>::value; } virtual bool takesArg() const { return !IsBool<M>::value; }
virtual IArgFunction<C>* clone() const { return new BoundUnaryMethod( *this ); } virtual IArgFunction<C>* clone() const { return new BoundUnaryMethod( *this ); }
void (C::*member)( M ); void (C::*member)( M );
@ -503,9 +485,6 @@ namespace Clara {
if( value ) if( value )
(p.*member)(); (p.*member)();
} }
virtual void setFlag( C& p ) const {
(p.*member)();
}
virtual bool takesArg() const { return false; } virtual bool takesArg() const { return false; }
virtual IArgFunction<C>* clone() const { return new BoundNullaryMethod( *this ); } virtual IArgFunction<C>* clone() const { return new BoundNullaryMethod( *this ); }
void (C::*member)(); void (C::*member)();
@ -520,9 +499,6 @@ namespace Clara {
if( value ) if( value )
function( obj ); function( obj );
} }
virtual void setFlag( C& p ) const {
function( p );
}
virtual bool takesArg() const { return false; } virtual bool takesArg() const { return false; }
virtual IArgFunction<C>* clone() const { return new BoundUnaryFunction( *this ); } virtual IArgFunction<C>* clone() const { return new BoundUnaryFunction( *this ); }
void (*function)( C& ); void (*function)( C& );
@ -536,11 +512,6 @@ namespace Clara {
convertInto( stringValue, value ); convertInto( stringValue, value );
function( obj, value ); function( obj, value );
} }
virtual void setFlag( C& obj ) const {
typename RemoveConstRef<T>::type value;
convertInto( true, value );
function( obj, value );
}
virtual bool takesArg() const { return !IsBool<T>::value; } virtual bool takesArg() const { return !IsBool<T>::value; }
virtual IArgFunction<C>* clone() const { return new BoundBinaryFunction( *this ); } virtual IArgFunction<C>* clone() const { return new BoundBinaryFunction( *this ); }
void (*function)( C&, T ); void (*function)( C&, T );
@ -548,8 +519,20 @@ namespace Clara {
} // namespace Detail } // namespace Detail
struct Parser { inline std::vector<std::string> argsToVector( int argc, char const* const* const argv ) {
Parser() : separators( " \t=:" ) {} std::vector<std::string> args( static_cast<std::size_t>( argc ) );
for( std::size_t i = 0; i < static_cast<std::size_t>( argc ); ++i )
args[i] = argv[i];
return args;
}
class Parser {
enum Mode { None, MaybeShortOpt, SlashOpt, ShortOpt, LongOpt, Positional };
Mode mode;
std::size_t from;
bool inQuotes;
public:
struct Token { struct Token {
enum Type { Positional, ShortOpt, LongOpt }; enum Type { Positional, ShortOpt, LongOpt };
@ -558,38 +541,75 @@ namespace Clara {
std::string data; std::string data;
}; };
void parseIntoTokens( int argc, char const* const argv[], std::vector<Parser::Token>& tokens ) const { Parser() : mode( None ), from( 0 ), inQuotes( false ){}
void parseIntoTokens( std::vector<std::string> const& args, std::vector<Token>& tokens ) {
const std::string doubleDash = "--"; const std::string doubleDash = "--";
for( int i = 1; i < argc && argv[i] != doubleDash; ++i ) for( std::size_t i = 1; i < args.size() && args[i] != doubleDash; ++i )
parseIntoTokens( argv[i] , tokens); parseIntoTokens( args[i], tokens);
} }
void parseIntoTokens( std::string arg, std::vector<Parser::Token>& tokens ) const {
while( !arg.empty() ) { void parseIntoTokens( std::string const& arg, std::vector<Token>& tokens ) {
Parser::Token token( Parser::Token::Positional, arg ); for( std::size_t i = 0; i <= arg.size(); ++i ) {
arg = ""; char c = arg[i];
if( token.data[0] == '-' ) { if( c == '"' )
if( token.data.size() > 1 && token.data[1] == '-' ) { inQuotes = !inQuotes;
token = Parser::Token( Parser::Token::LongOpt, token.data.substr( 2 ) ); mode = handleMode( i, c, arg, tokens );
}
else {
token = Parser::Token( Parser::Token::ShortOpt, token.data.substr( 1 ) );
if( token.data.size() > 1 && separators.find( token.data[1] ) == std::string::npos ) {
arg = "-" + token.data.substr( 1 );
token.data = token.data.substr( 0, 1 );
} }
} }
} Mode handleMode( std::size_t i, char c, std::string const& arg, std::vector<Token>& tokens ) {
if( token.type != Parser::Token::Positional ) { switch( mode ) {
std::size_t pos = token.data.find_first_of( separators ); case None: return handleNone( i, c );
if( pos != std::string::npos ) { case MaybeShortOpt: return handleMaybeShortOpt( i, c );
arg = token.data.substr( pos+1 ); case ShortOpt:
token.data = token.data.substr( 0, pos ); case LongOpt:
case SlashOpt: return handleOpt( i, c, arg, tokens );
case Positional: return handlePositional( i, c, arg, tokens );
default: throw std::logic_error( "Unknown mode" );
} }
} }
tokens.push_back( token );
Mode handleNone( std::size_t i, char c ) {
if( inQuotes ) {
from = i;
return Positional;
}
switch( c ) {
case '-': return MaybeShortOpt;
#ifdef CLARA_PLATFORM_WINDOWS
case '/': from = i+1; return SlashOpt;
#endif
default: from = i; return Positional;
} }
} }
std::string separators; Mode handleMaybeShortOpt( std::size_t i, char c ) {
switch( c ) {
case '-': from = i+1; return LongOpt;
default: from = i; return ShortOpt;
}
}
Mode handleOpt( std::size_t i, char c, std::string const& arg, std::vector<Token>& tokens ) {
if( std::string( " \t:=\0", 5 ).find( c ) == std::string::npos )
return mode;
std::string optName = arg.substr( from, i-from );
if( mode == ShortOpt )
for( std::size_t j = 0; j < optName.size(); ++j )
tokens.push_back( Token( Token::ShortOpt, optName.substr( j, 1 ) ) );
else if( mode == SlashOpt && optName.size() == 1 )
tokens.push_back( Token( Token::ShortOpt, optName ) );
else
tokens.push_back( Token( Token::LongOpt, optName ) );
return None;
}
Mode handlePositional( std::size_t i, char c, std::string const& arg, std::vector<Token>& tokens ) {
if( inQuotes || std::string( " \t\0", 3 ).find( c ) == std::string::npos )
return mode;
std::string data = arg.substr( from, i-from );
tokens.push_back( Token( Token::Positional, data ) );
return None;
}
}; };
template<typename ConfigT> template<typename ConfigT>
@ -894,21 +914,21 @@ namespace Clara {
return oss.str(); return oss.str();
} }
ConfigT parse( int argc, char const* const argv[] ) const { ConfigT parse( std::vector<std::string> const& args ) const {
ConfigT config; ConfigT config;
parseInto( argc, argv, config ); parseInto( args, config );
return config; return config;
} }
std::vector<Parser::Token> parseInto( int argc, char const* argv[], ConfigT& config ) const { std::vector<Parser::Token> parseInto( std::vector<std::string> const& args, ConfigT& config ) const {
std::string processName = argv[0]; std::string processName = args[0];
std::size_t lastSlash = processName.find_last_of( "/\\" ); std::size_t lastSlash = processName.find_last_of( "/\\" );
if( lastSlash != std::string::npos ) if( lastSlash != std::string::npos )
processName = processName.substr( lastSlash+1 ); processName = processName.substr( lastSlash+1 );
m_boundProcessName.set( config, processName ); m_boundProcessName.set( config, processName );
std::vector<Parser::Token> tokens; std::vector<Parser::Token> tokens;
Parser parser; Parser parser;
parser.parseIntoTokens( argc, argv, tokens ); parser.parseIntoTokens( args, tokens );
return populate( tokens, config ); return populate( tokens, config );
} }
@ -939,7 +959,7 @@ namespace Clara {
arg.boundField.set( config, tokens[++i].data ); arg.boundField.set( config, tokens[++i].data );
} }
else { else {
arg.boundField.setFlag( config ); arg.boundField.set( config, "true" );
} }
break; break;
} }

View File

@ -25,7 +25,7 @@ CATCH_REGISTER_TAG_ALIAS( "[@tricky]", "[tricky]~[.]" )
template<size_t size> template<size_t size>
void parseIntoConfig( const char * (&argv)[size], Catch::ConfigData& config ) { void parseIntoConfig( const char * (&argv)[size], Catch::ConfigData& config ) {
Catch::Clara::CommandLine<Catch::ConfigData> parser = Catch::makeCommandLineParser(); Catch::Clara::CommandLine<Catch::ConfigData> parser = Catch::makeCommandLineParser();
parser.parseInto( size, argv, config ); parser.parseInto( Catch::Clara::argsToVector( size, argv ), config );
} }
template<size_t size> template<size_t size>