diff --git a/include/argparse.hpp b/include/argparse.hpp index aef3c38..466e7c0 100644 --- a/include/argparse.hpp +++ b/include/argparse.hpp @@ -39,6 +39,7 @@ SOFTWARE. #include #include #include +#include namespace argparse { @@ -77,8 +78,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; } @@ -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,14 +474,16 @@ 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]; for (int i = 1; i < argc; i++) { 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()) { @@ -553,9 +563,9 @@ 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; - print_help(); - exit(0); + std::stringstream stream; + stream << "error: unexpected positional argument " << argv[i] << std::endl; + throw std::runtime_error(stream.str()); } auto tArgument = mPositionalArguments[mNextPositionalArgument]; auto tCount = tArgument->mNumArgs - tArgument->mRawValues.size(); @@ -587,15 +597,18 @@ 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) { 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(); - exit(0); + throw std::runtime_error(stream.str()); } } @@ -606,11 +619,11 @@ 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(); - exit(0); + throw std::runtime_error(stream.str()); } } } @@ -657,4 +670,13 @@ class ArgumentParser { std::map> mArgumentMap; }; +#define PARSE_ARGS(parser, argc, argv) \ +try { \ + parser.parse_args(argc, argv); \ +} catch (const std::runtime_error& err) { \ + std::cerr << err.what() << std::endl; \ + parser.print_help(); \ + exit(0); \ +} + }