mirror of https://github.com/catchorg/Catch2.git
More CmdLine work
- Support for non string values - Support for chaining parsers
This commit is contained in:
parent
26ae11774b
commit
46846a47f3
|
@ -10,7 +10,6 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "catch.hpp"
|
#include "catch.hpp"
|
||||||
//#include "catch_ptr.hpp"
|
|
||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
|
|
||||||
|
@ -51,6 +50,18 @@ protected:
|
||||||
int m_weight;
|
int m_weight;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class Opt {
|
class Opt {
|
||||||
public:
|
public:
|
||||||
|
@ -87,14 +98,12 @@ private:
|
||||||
virtual ~IField() {}
|
virtual ~IField() {}
|
||||||
virtual bool parseInto( std::string const& _arg, T& _config ) const = 0;
|
virtual bool parseInto( std::string const& _arg, T& _config ) const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename M>
|
template<typename M>
|
||||||
struct Field : IField {
|
struct Field : IField {
|
||||||
Field( M const& _member ) : member( _member ) {}
|
Field( M const& _member ) : member( _member ) {}
|
||||||
bool parseInto( std::string const& _arg, T& _config ) const {
|
bool parseInto( std::string const& _arg, T& _config ) const {
|
||||||
std::stringstream ss;
|
return convertInto( _arg, _config.*member );
|
||||||
ss << _arg;
|
|
||||||
ss >> _config.*member;
|
|
||||||
return !ss.fail();
|
|
||||||
}
|
}
|
||||||
M member;
|
M member;
|
||||||
};
|
};
|
||||||
|
@ -146,40 +155,58 @@ public:
|
||||||
if( !_opt.longOpt().empty() )
|
if( !_opt.longOpt().empty() )
|
||||||
m_optionsByName.insert( std::make_pair( "--" + _opt.longOpt(), &m_allOptionParsers.back() ) );
|
m_optionsByName.insert( std::make_pair( "--" + _opt.longOpt(), &m_allOptionParsers.back() ) );
|
||||||
}
|
}
|
||||||
bool parseArgs( int argc, const char* const argv[], T& config ) {
|
|
||||||
for( int i = 0; i < argc; ++i ) {
|
void parseArgs( int argc, const char* const argv[], T& _config ) {
|
||||||
std::string fullArg = argv[i];
|
for( int i = 0; i < argc; ++i )
|
||||||
if( fullArg[0] == '-' ) {
|
parseArg( argv[i], _config );
|
||||||
std::string args, optName;
|
|
||||||
std::size_t pos = fullArg.find( ':' );
|
|
||||||
if( pos == std::string::npos ) {
|
|
||||||
optName = fullArg;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
optName = fullArg.substr(0, pos );
|
|
||||||
args = fullArg.substr( pos+1 );
|
|
||||||
}
|
|
||||||
typename std::map<std::string, Opt<T>*>::const_iterator it = m_optionsByName.find( optName );
|
|
||||||
bool used = false;
|
|
||||||
if( it != m_optionsByName.end() ) {
|
|
||||||
try {
|
|
||||||
used = it->second->parseInto( args, config );
|
|
||||||
}
|
|
||||||
catch( std::exception& ex ) {
|
|
||||||
throw std::domain_error( "Error in " + optName + " option: " + ex.what() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if( !used )
|
|
||||||
m_unusedOpts.push_back( fullArg );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
m_args.push_back( fullArg );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void parseArgs( std::vector<std::string> const& _args, T& _config ) {
|
||||||
|
for( std::vector<std::string>::const_iterator
|
||||||
|
it = _args.begin(), itEnd = _args.end();
|
||||||
|
it != itEnd;
|
||||||
|
++it )
|
||||||
|
parseArg( *it, _config );
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename U>
|
||||||
|
void parseRemainingArgs( CommandLineParser<U>& _parser, T& _config ) {
|
||||||
|
parseArgs( _parser.m_unusedOpts, _config );
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseArg( std::string const& _arg, T& _config ) {
|
||||||
|
if( _arg[0] == '-' ) {
|
||||||
|
std::string args, optName;
|
||||||
|
std::size_t pos = _arg.find( ':' );
|
||||||
|
if( pos == std::string::npos ) {
|
||||||
|
optName = _arg;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
optName = _arg.substr(0, pos );
|
||||||
|
args = _arg.substr( pos+1 );
|
||||||
|
}
|
||||||
|
typename std::map<std::string, Opt<T>*>::const_iterator it = m_optionsByName.find( optName );
|
||||||
|
bool used = false;
|
||||||
|
if( it != m_optionsByName.end() ) {
|
||||||
|
try {
|
||||||
|
used = it->second->parseInto( args, _config );
|
||||||
|
}
|
||||||
|
catch( std::exception& ex ) {
|
||||||
|
throw std::domain_error( "Error in " + optName + " option: " + ex.what() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( !used )
|
||||||
|
m_unusedOpts.push_back( _arg );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_args.push_back( _arg );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
template<typename U>
|
||||||
|
friend class CommandLineParser;
|
||||||
|
|
||||||
std::vector<Opt<T> > m_allOptionParsers;
|
std::vector<Opt<T> > m_allOptionParsers;
|
||||||
std::map<std::string, Opt<T>*> m_optionsByName;
|
std::map<std::string, Opt<T>*> m_optionsByName;
|
||||||
std::vector<std::string> m_args;
|
std::vector<std::string> m_args;
|
||||||
|
@ -197,6 +224,10 @@ struct TestOpt {
|
||||||
int number;
|
int number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct TestOpt2 {
|
||||||
|
std::string description;
|
||||||
|
};
|
||||||
|
|
||||||
TEST_CASE( "Arg" ) {
|
TEST_CASE( "Arg" ) {
|
||||||
SECTION( "pre and post" ) {
|
SECTION( "pre and post" ) {
|
||||||
Catch::ArgData preAndPost( "prefix<arg>postfix" );
|
Catch::ArgData preAndPost( "prefix<arg>postfix" );
|
||||||
|
@ -291,11 +322,9 @@ TEST_CASE( "cmdline", "" ) {
|
||||||
CHECK( config.streamName == "stdout" );
|
CHECK( config.streamName == "stdout" );
|
||||||
}
|
}
|
||||||
|
|
||||||
Catch::Opt<TestOpt> opt2( "a number" );
|
parser.addOption( Catch::Opt<TestOpt>( "a number" )
|
||||||
opt2.shortOpt( "n" )
|
.shortOpt( "n" )
|
||||||
.addArg( "<an integral value>", &TestOpt::number );
|
.addArg( "<an integral value>", &TestOpt::number ) );
|
||||||
|
|
||||||
parser.addOption( opt2 );
|
|
||||||
|
|
||||||
SECTION( "a number" ) {
|
SECTION( "a number" ) {
|
||||||
const char* argv[] = { "test", "-n:42" };
|
const char* argv[] = { "test", "-n:42" };
|
||||||
|
@ -310,4 +339,24 @@ TEST_CASE( "cmdline", "" ) {
|
||||||
CHECK( config.number == 0 );
|
CHECK( config.number == 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION( "two parsers" ) {
|
||||||
|
|
||||||
|
TestOpt config1;
|
||||||
|
TestOpt2 config2;
|
||||||
|
Catch::CommandLineParser<TestOpt2> parser2;
|
||||||
|
parser2.addOption( Catch::Opt<TestOpt2>( "description" )
|
||||||
|
.shortOpt( "d" )
|
||||||
|
.longOpt( "description" )
|
||||||
|
.addArg( "<some text>", &TestOpt2::description ) );
|
||||||
|
|
||||||
|
const char* argv[] = { "test", "-n:42", "-d:some text" };
|
||||||
|
|
||||||
|
parser.parseArgs( sizeof(argv)/sizeof(char*), argv, config1 );
|
||||||
|
CHECK( config1.number == 42 );
|
||||||
|
|
||||||
|
parser2.parseRemainingArgs( parser, config2 );
|
||||||
|
CHECK( config2.description == "some text" );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue