diff --git a/README.md b/README.md index 00b5c7a..62dbb65 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ int main(int argc, char *argv[]) { } catch (const std::runtime_error& err) { std::cout << err.what() << std::endl; - program.print_help(); + std::cout << program; exit(0); } @@ -100,7 +100,7 @@ try { } catch (const std::runtime_error& err) { std::cout << err.what() << std::endl; - program.print_help(); + std::cout << program; exit(0); } @@ -161,7 +161,7 @@ try { } catch (const std::runtime_error& err) { std::cout << err.what() << std::endl; - program.print_help(); + std::cout << program; exit(0); } @@ -194,7 +194,7 @@ try { } catch (const std::runtime_error& err) { std::cout << err.what() << std::endl; - program.print_help(); + std::cout << program; exit(0); } @@ -221,7 +221,7 @@ The square of 4 is 16 ### Printing Help -```ArgumentParser.print_help()``` print a help message, including the program usage and information about the arguments registered with the ArgumentParser. For the previous example, here's the default help message: +`std::cout << program` prints a help message, including the program usage and information about the arguments registered with the `ArgumentParser`. For the previous example, here's the default help message: ``` $ ./main --help @@ -235,6 +235,8 @@ Optional arguments: -v, --verbose enable verbose logging ``` +You may also get the help message in string via `program.help().str()`. + ### List of Arguments ArgumentParser objects usually associate a single command-line argument with a single action to be taken. The ```.nargs``` associates a different number of command-line arguments with a single action. When using ```nargs(N)```, N arguments from the command line will be gathered together into a list. @@ -251,7 +253,7 @@ try { } catch (const std::runtime_error& err) { std::cout << err.what() << std::endl; - program.print_help(); + std::cout << program; exit(0); } @@ -280,7 +282,7 @@ try { } catch (const std::runtime_error& err) { std::cout << err.what() << std::endl; - program.print_help(); + std::cout << program; exit(0); } @@ -312,7 +314,7 @@ try { } catch (const std::runtime_error& err) { std::cout << err.what() << std::endl; - program.print_help(); + std::cout << program; exit(0); } @@ -388,7 +390,7 @@ try { } catch (const std::runtime_error& err) { std::cout << err.what() << std::endl; - program.print_help(); + std::cout << program; exit(0); } @@ -424,7 +426,7 @@ try { } catch (const std::runtime_error& err) { std::cout << err.what() << std::endl; - program.print_help(); + std::cout << program; exit(0); } @@ -466,7 +468,7 @@ try { } catch (const std::runtime_error& err) { std::cout << err.what() << std::endl; - program.print_help(); + std::cout << program; exit(0); } diff --git a/include/argparse.hpp b/include/argparse.hpp index 7205b74..f3ad25a 100644 --- a/include/argparse.hpp +++ b/include/argparse.hpp @@ -31,7 +31,6 @@ SOFTWARE. #include #include #include -#include #include #include #include @@ -75,8 +74,12 @@ template using enable_if_not_container = std::enable_if_t, T>; } // namespace +class ArgumentParser; + class Argument { friend class ArgumentParser; + friend auto operator<<(std::ostream &, ArgumentParser const &) + -> std::ostream &; public: Argument() = default; @@ -412,39 +415,54 @@ public: throw std::logic_error("No such argument"); } + // Print help message + friend auto operator<<(std::ostream &stream, const ArgumentParser &parser) + -> std::ostream & { + if (auto sen = std::ostream::sentry(stream)) { + stream.setf(std::ios_base::left); + stream << "Usage: " << parser.mProgramName << " [options] "; + size_t tLongestArgumentLength = parser.get_length_of_longest_argument(); + + for (const auto &argument : parser.mPositionalArguments) { + stream << argument->mNames.front() << " "; + } + stream << "\n\n"; + + if (!parser.mPositionalArguments.empty()) + stream << "Positional arguments:\n"; + + for (const auto &mPositionalArgument : parser.mPositionalArguments) { + stream.width(tLongestArgumentLength); + stream << *mPositionalArgument; + } + + if (!parser.mOptionalArguments.empty()) + stream << (parser.mPositionalArguments.empty() ? "" : "\n") + << "Optional arguments:\n"; + + for (const auto &mOptionalArgument : parser.mOptionalArguments) { + stream.width(tLongestArgumentLength); + stream << *mOptionalArgument; + } + } + + return stream; + } + + // Format help message + auto help() const -> std::ostringstream { + std::ostringstream out; + out << *this; + return out; + } + // Printing the one and only help message // I've stuck with a simple message format, nothing fancy. - // TODO: support user-defined help and usage messages for the ArgumentParser - std::string print_help() { - std::stringstream stream; - stream << std::left; - stream << "Usage: " << mProgramName << " [options] "; - size_t tLongestArgumentLength = get_length_of_longest_argument(); - - for (const auto &argument : mPositionalArguments) { - stream << argument->mNames.front() << " "; - } - stream << "\n\n"; - - if (!mPositionalArguments.empty()) - stream << "Positional arguments:\n"; - - for (const auto &mPositionalArgument : mPositionalArguments) { - stream.width(tLongestArgumentLength); - stream << *mPositionalArgument; - } - - if (!mOptionalArguments.empty()) - stream << (mPositionalArguments.empty() ? "" : "\n") - << "Optional arguments:\n"; - - for (const auto &mOptionalArgument : mOptionalArguments) { - stream.width(tLongestArgumentLength); - stream << *mOptionalArgument; - } - - std::cout << stream.str(); - return stream.str(); + [[deprecated("Use cout << program; instead. See also help().")]] std::string + print_help() { + auto out = help(); + std::cout << out.rdbuf(); + return out.str(); } private: @@ -507,7 +525,7 @@ private: } // Used by print_help. - size_t get_length_of_longest_argument() { + size_t get_length_of_longest_argument() const { if (mArgumentMap.empty()) return 0; std::vector argumentLengths(mArgumentMap.size()); diff --git a/test/main.cpp b/test/main.cpp index 3dbb7cd..0ffa4a6 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -1,6 +1,7 @@ #define CATCH_CONFIG_MAIN #include #include +#include #include #include #include @@ -11,4 +12,4 @@ #include #include #include -#include +#include \ No newline at end of file diff --git a/test/test_help.hpp b/test/test_help.hpp new file mode 100644 index 0000000..d547630 --- /dev/null +++ b/test/test_help.hpp @@ -0,0 +1,18 @@ +#pragma once +#include +#include + +TEST_CASE("Users can format help message", "[help]") { + argparse::ArgumentParser program("test"); + program.add_argument("input") + .help("positional input"); + program.add_argument("-c") + .help("optional input"); + + std::ostringstream s; + s << program; + REQUIRE_FALSE(s.str().empty()); + + auto msg = program.help().str(); + REQUIRE(msg == s.str()); +}