From 94e4458641d28df605c41d256921259006a2c5f6 Mon Sep 17 00:00:00 2001 From: Stephan van Veen Date: Sat, 11 May 2019 00:38:01 +0200 Subject: [PATCH 1/5] Make use of move semantics --- include/argparse.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/argparse.hpp b/include/argparse.hpp index aef3c38..bd1e628 100644 --- a/include/argparse.hpp +++ b/include/argparse.hpp @@ -77,8 +77,8 @@ public: , mIsOptional((is_optional(args) || ...)) {} - Argument& help(const std::string& aHelp) { - mHelp = aHelp; + Argument& help(std::string aHelp) { + mHelp = std::move(aHelp); return *this; } From a0fa02503ef139c06ef87b71bd331b5a95f323dc Mon Sep 17 00:00:00 2001 From: Stephan van Veen Date: Sat, 11 May 2019 11:23:44 +0200 Subject: [PATCH 2/5] Throw std::runtime_error instead of calling exit(0) --- include/argparse.hpp | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/include/argparse.hpp b/include/argparse.hpp index bd1e628..28ca037 100644 --- a/include/argparse.hpp +++ b/include/argparse.hpp @@ -39,6 +39,7 @@ SOFTWARE. #include #include #include +#include namespace argparse { @@ -321,15 +322,19 @@ class ArgumentParser { add_parents(Fargs...); } - // Call parse_args_internal - which does all the work - // Then, validate the parsed arguments - // This variant is used mainly for testing + /* Call parse_args_internal - which does all the work + * Then, validate the parsed arguments + * This variant is used mainly for testing + * @throws std::runtime_error in case of any invalid argument + */ void parse_args(const std::vector& aArguments) { parse_args_internal(aArguments); parse_args_validate(); } - // Main entry point for parsing command-line arguments using this ArgumentParser + /* Main entry point for parsing command-line arguments using this ArgumentParser + * @throws std::runtime_error in case of any invalid argument + */ void parse_args(int argc, char * argv[]) { parse_args_internal(argc, argv); parse_args_validate(); @@ -458,6 +463,9 @@ class ArgumentParser { return (tIterator != mArgumentMap.end()); } + /* + * @throws std::runtime_error in case of any invalid argument + */ void parse_args_internal(const std::vector& aArguments) { std::vector argv; for (const auto& arg : aArguments) @@ -466,6 +474,9 @@ class ArgumentParser { return parse_args_internal(int(argv.size()) - 1, argv.data()); } + /* + * @throws std::runtime_error in case of any invalid argument + */ void parse_args_internal(int argc, char * argv[]) { if (mProgramName.empty() && argc > 0) mProgramName = argv[0]; @@ -473,7 +484,7 @@ class ArgumentParser { auto tCurrentArgument = std::string(argv[i]); if (tCurrentArgument == "-h" || tCurrentArgument == "--help") { print_help(); - exit(0); + throw std::runtime_error("help called"); } auto tIterator = mArgumentMap.find(argv[i]); if (tIterator != mArgumentMap.end()) { @@ -555,7 +566,7 @@ class ArgumentParser { if (mNextPositionalArgument >= mPositionalArguments.size()) { std::cout << "error: unexpected positional argument " << argv[i] << std::endl; print_help(); - exit(0); + throw std::runtime_error("unexpected positional argument"); } auto tArgument = mPositionalArguments[mNextPositionalArgument]; auto tCount = tArgument->mNumArgs - tArgument->mRawValues.size(); @@ -587,6 +598,9 @@ class ArgumentParser { } } + /* + * @throws std::runtime_error in case of any invalid argument + */ void parse_args_validate() { // Check if all positional arguments are parsed for (const auto& tArgument : mPositionalArguments) { @@ -595,7 +609,7 @@ class ArgumentParser { << tArgument->mNumArgs << (tArgument->mNumArgs == 1 ? " argument. " : " arguments. ") << tArgument->mValues.size() << " provided.\n" << std::endl; print_help(); - exit(0); + throw std::runtime_error("wrong number of arguments"); } } @@ -610,7 +624,7 @@ class ArgumentParser { << tArgument->mNumArgs << (tArgument->mNumArgs == 1 ? " argument. " : " arguments. ") << tArgument->mValues.size() << " provided.\n" << std::endl; print_help(); - exit(0); + throw std::runtime_error("wrong number of arguments"); } } } From 3c65c5dcaba68e5d44d3b45e632ed43328bcb478 Mon Sep 17 00:00:00 2001 From: Stephan van Veen Date: Sat, 11 May 2019 11:24:41 +0200 Subject: [PATCH 3/5] Add MACRO which calls exit(0) on any runtime_error in parse_args --- include/argparse.hpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/argparse.hpp b/include/argparse.hpp index 28ca037..c2a17f3 100644 --- a/include/argparse.hpp +++ b/include/argparse.hpp @@ -671,4 +671,11 @@ class ArgumentParser { std::map> mArgumentMap; }; +#define PARSE_ARGS(parser, argc, argv) \ +try { \ + parser.parse_args(argc, argv); \ +} catch (const std::runtime_error& err) { \ + exit(0); \ +} + } From 42ff1867432b35b5517a516572cb10742e0288c9 Mon Sep 17 00:00:00 2001 From: Stephan van Veen Date: Sat, 11 May 2019 12:45:10 +0200 Subject: [PATCH 4/5] Put error message into exception instead of printing --- include/argparse.hpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/include/argparse.hpp b/include/argparse.hpp index c2a17f3..d69090f 100644 --- a/include/argparse.hpp +++ b/include/argparse.hpp @@ -564,9 +564,10 @@ class ArgumentParser { // This is a positional argument. // Parse and save into mPositionalArguments vector if (mNextPositionalArgument >= mPositionalArguments.size()) { - std::cout << "error: unexpected positional argument " << argv[i] << std::endl; + std::stringstream stream; + stream << "error: unexpected positional argument " << argv[i] << std::endl; print_help(); - throw std::runtime_error("unexpected positional argument"); + throw std::runtime_error(stream.str()); } auto tArgument = mPositionalArguments[mNextPositionalArgument]; auto tCount = tArgument->mNumArgs - tArgument->mRawValues.size(); @@ -605,11 +606,12 @@ class ArgumentParser { // Check if all positional arguments are parsed for (const auto& tArgument : mPositionalArguments) { if (tArgument->mValues.size() != tArgument->mNumArgs) { - std::cout << "error: " << tArgument->mUsedName << ": expected " + std::stringstream stream; + stream << "error: " << tArgument->mUsedName << ": expected " << tArgument->mNumArgs << (tArgument->mNumArgs == 1 ? " argument. " : " arguments. ") << tArgument->mValues.size() << " provided.\n" << std::endl; print_help(); - throw std::runtime_error("wrong number of arguments"); + throw std::runtime_error(stream.str()); } } @@ -620,11 +622,12 @@ class ArgumentParser { // All cool if there's a default value to return // If no default value, then there's a problem if (!tArgument->mDefaultValue.has_value()) { - std::cout << "error: " << tArgument->mUsedName << ": expected " + std::stringstream stream; + stream << "error: " << tArgument->mUsedName << ": expected " << tArgument->mNumArgs << (tArgument->mNumArgs == 1 ? " argument. " : " arguments. ") << tArgument->mValues.size() << " provided.\n" << std::endl; print_help(); - throw std::runtime_error("wrong number of arguments"); + throw std::runtime_error(stream.str()); } } } @@ -675,6 +678,7 @@ class ArgumentParser { try { \ parser.parse_args(argc, argv); \ } catch (const std::runtime_error& err) { \ + std::cerr << err.what() << std::endl; \ exit(0); \ } From 12ad27f9c3f6e3a0ef2bc75ed27df47c63e9adde Mon Sep 17 00:00:00 2001 From: Stephan van Veen Date: Sat, 11 May 2019 13:11:49 +0200 Subject: [PATCH 5/5] Don't call print_help inside ArgumentParser --- include/argparse.hpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/include/argparse.hpp b/include/argparse.hpp index d69090f..466e7c0 100644 --- a/include/argparse.hpp +++ b/include/argparse.hpp @@ -483,7 +483,6 @@ class ArgumentParser { for (int i = 1; i < argc; i++) { auto tCurrentArgument = std::string(argv[i]); if (tCurrentArgument == "-h" || tCurrentArgument == "--help") { - print_help(); throw std::runtime_error("help called"); } auto tIterator = mArgumentMap.find(argv[i]); @@ -566,7 +565,6 @@ class ArgumentParser { if (mNextPositionalArgument >= mPositionalArguments.size()) { std::stringstream stream; stream << "error: unexpected positional argument " << argv[i] << std::endl; - print_help(); throw std::runtime_error(stream.str()); } auto tArgument = mPositionalArguments[mNextPositionalArgument]; @@ -610,7 +608,6 @@ class ArgumentParser { stream << "error: " << tArgument->mUsedName << ": expected " << tArgument->mNumArgs << (tArgument->mNumArgs == 1 ? " argument. " : " arguments. ") << tArgument->mValues.size() << " provided.\n" << std::endl; - print_help(); throw std::runtime_error(stream.str()); } } @@ -626,7 +623,6 @@ class ArgumentParser { stream << "error: " << tArgument->mUsedName << ": expected " << tArgument->mNumArgs << (tArgument->mNumArgs == 1 ? " argument. " : " arguments. ") << tArgument->mValues.size() << " provided.\n" << std::endl; - print_help(); throw std::runtime_error(stream.str()); } } @@ -679,6 +675,7 @@ try { \ parser.parse_args(argc, argv); \ } catch (const std::runtime_error& err) { \ std::cerr << err.what() << std::endl; \ + parser.print_help(); \ exit(0); \ }