From 46846a47f3b0c89f573fb169d7421b7eaca3b3f8 Mon Sep 17 00:00:00 2001 From: Phil Nash Date: Mon, 29 Apr 2013 19:26:18 +0100 Subject: [PATCH] More CmdLine work - Support for non string values - Support for chaining parsers --- projects/SelfTest/CmdLineTests.cpp | 131 ++++++++++++++++++++--------- 1 file changed, 90 insertions(+), 41 deletions(-) diff --git a/projects/SelfTest/CmdLineTests.cpp b/projects/SelfTest/CmdLineTests.cpp index 9952f36c..a7ca1e2c 100644 --- a/projects/SelfTest/CmdLineTests.cpp +++ b/projects/SelfTest/CmdLineTests.cpp @@ -10,7 +10,6 @@ #endif #include "catch.hpp" -//#include "catch_ptr.hpp" namespace Catch { @@ -51,6 +50,18 @@ protected: int m_weight; }; +template +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 class Opt { public: @@ -87,14 +98,12 @@ private: virtual ~IField() {} virtual bool parseInto( std::string const& _arg, T& _config ) const = 0; }; + template struct Field : IField { Field( M const& _member ) : member( _member ) {} bool parseInto( std::string const& _arg, T& _config ) const { - std::stringstream ss; - ss << _arg; - ss >> _config.*member; - return !ss.fail(); + return convertInto( _arg, _config.*member ); } M member; }; @@ -146,40 +155,58 @@ public: if( !_opt.longOpt().empty() ) 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 ) { - std::string fullArg = argv[i]; - if( fullArg[0] == '-' ) { - 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*>::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( int argc, const char* const argv[], T& _config ) { + for( int i = 0; i < argc; ++i ) + parseArg( argv[i], _config ); } + void parseArgs( std::vector const& _args, T& _config ) { + for( std::vector::const_iterator + it = _args.begin(), itEnd = _args.end(); + it != itEnd; + ++it ) + parseArg( *it, _config ); + } + + template + void parseRemainingArgs( CommandLineParser& _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*>::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: + template + friend class CommandLineParser; + std::vector > m_allOptionParsers; std::map*> m_optionsByName; std::vector m_args; @@ -197,6 +224,10 @@ struct TestOpt { int number; }; +struct TestOpt2 { + std::string description; +}; + TEST_CASE( "Arg" ) { SECTION( "pre and post" ) { Catch::ArgData preAndPost( "prefixpostfix" ); @@ -291,11 +322,9 @@ TEST_CASE( "cmdline", "" ) { CHECK( config.streamName == "stdout" ); } - Catch::Opt opt2( "a number" ); - opt2.shortOpt( "n" ) - .addArg( "", &TestOpt::number ); - - parser.addOption( opt2 ); + parser.addOption( Catch::Opt( "a number" ) + .shortOpt( "n" ) + .addArg( "", &TestOpt::number ) ); SECTION( "a number" ) { const char* argv[] = { "test", "-n:42" }; @@ -310,4 +339,24 @@ TEST_CASE( "cmdline", "" ) { CHECK( config.number == 0 ); } + SECTION( "two parsers" ) { + + TestOpt config1; + TestOpt2 config2; + Catch::CommandLineParser parser2; + parser2.addOption( Catch::Opt( "description" ) + .shortOpt( "d" ) + .longOpt( "description" ) + .addArg( "", &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" ); + + } + }