From f92b5a516eddbdf33a69e95da6678eb9c9ef8c8c Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Tue, 4 Jun 2019 13:31:28 +0200 Subject: Add support for printing automatically generated help texts. --- example.cpp | 81 ++++++++++++++++++++++++++++++++++++------------------------ getoptpp.hpp | 55 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 101 insertions(+), 35 deletions(-) diff --git a/example.cpp b/example.cpp index e273c2f..bd94585 100644 --- a/example.cpp +++ b/example.cpp @@ -6,38 +6,55 @@ int main(int argc, char* argv[]) { int verbose_flag; dg::Options opt; - opt.add("verbose", no_argument, &verbose_flag, 1, [&]() { - std::cout << "verbose: " << verbose_flag << "\n"; - return 0; - }); - opt.add("brief", no_argument, &verbose_flag, 0, [&]() { - std::cout << "brief: " << verbose_flag << "\n"; - return 0; - }); - opt.add("add", no_argument, 'a', [&]() { - std::cout << "add\n"; - return 0; - }); - opt.add("append", no_argument, 'b', [&]() { - std::cout << "append\n"; - return 0; - }); - opt.add("delete", required_argument, 'd', [&]() { - std::cout << "delete: " << optarg << "\n"; - return 0; - }); - opt.add("create", required_argument, 'c', [&]() { - std::cout << "create: " << optarg << "\n"; - return 0; - }); - opt.add("file", required_argument, 'f', [&]() { - std::cout << "file: " << optarg << "\n"; - return 0; - }); - opt.add("help", no_argument, '?', [&]() { - std::cout << "usage stuff\n"; - return 0; - }); + opt.add("verbose", no_argument, &verbose_flag, 1, + "Be verbose.", + [&]() { + std::cout << "verbose: " << verbose_flag << "\n"; + return 0; + }); + opt.add("brief", no_argument, &verbose_flag, 0, + "Be brief.", + [&]() { + std::cout << "brief: " << verbose_flag << "\n"; + return 0; + }); + opt.add("add", no_argument, 'a', + "Add an entry.", + [&]() { + std::cout << "add\n"; + return 0; + }); + opt.add("append", no_argument, 'b', + "Append an entry.", + [&]() { + std::cout << "append\n"; + return 0; + }); + opt.add("delete", optional_argument, 'd', + "Delete an entry.", + [&]() { + std::cout << "delete: " << optarg << "\n"; + return 0; + }); + opt.add("create", required_argument, 'c', + "Create an entry.", + [&]() { + std::cout << "create: " << optarg << "\n"; + return 0; + }); + opt.add("file", required_argument, 'f', + "Some file argument.", + [&]() { + std::cout << "file: " << optarg << "\n"; + return 0; + }); + opt.add("help", no_argument, 'h', + "Print this help text.", + [&]() { + std::cout << "usage stuff\n"; + opt.help(); + return 0; + }); opt.process(argc, argv); for(auto const &arg : opt.arguments()) diff --git a/getoptpp.hpp b/getoptpp.hpp index 8788dde..a2dca7f 100644 --- a/getoptpp.hpp +++ b/getoptpp.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include namespace dg { @@ -16,19 +17,22 @@ class Options { /// @param name name of the option /// @param has_arg kind of arguments that are used (no_argument, required_argument, optional_argument) /// @param val identifies the option (see getopt documentation) + /// @param help_text help text for this option /// @param handle lambda that is invoked when the option occures - void add(std::string const & name, int has_arg, int val, Handle handle) { - add(name, has_arg, nullptr, val, handle); + void add(std::string const & name, int has_arg, int val, std::string const & help_text, Handle handle) { + add(name, has_arg, nullptr, val, help_text, handle); } /// @param name name of the option /// @param has_arg kind of arguments that are used (no_argument, required_argument, optional_argument) /// @param flag pointer that is set after the option occured /// @param val value for the flag to be set + /// @param help_text help text for this option /// @param handle lambda that is invoked when the option occures - void add(std::string const & name, int has_arg, int* flag, int val, Handle handle) { + void add(std::string const & name, int has_arg, int* flag, int val, std::string const & help_text, Handle handle) { // create a new option from the args options.push_back({name.data(), has_arg, flag, val}); + help_texts.push_back(help_text); int index = val; // let val be the option's unique identifier if (flag != nullptr) { @@ -95,14 +99,59 @@ class Options { // remove terminating option options.pop_back(); assert(options.size() == handles.size()); + assert(options.size() == help_texts.size()); return 0; } const std::vector arguments() const { return args; } + void help() { + std::size_t width = 0; + for(auto const & opt : options) { + if(opt.name == nullptr) { + continue; // skip terminating option + } + width = std::max(width, std::strlen(opt.name) + (opt.has_arg != no_argument ? 0 : 3)); + } + width += 1; + + int index = 0; + for(auto const & help_text : help_texts) { + auto const & opt = options.at(index); + std::string args; + switch(opt.has_arg) { + case required_argument: + args = ""; + break; + case optional_argument: + args = "[x]"; + break; + case no_argument: + default: + break; + } + + std::string padding; + std::size_t pad_size = width - (std::strlen(opt.name) + args.size()); + padding.append(pad_size, ' '); + if(opt.val >= 33 && opt.val <= 126) + { + std::cout << " -" << static_cast(opt.val) << ", --" << opt.name << " " << args << padding; + } + else + { + std::cout << " --" << opt.name << " " << args << padding; + } + + std::cout << help_text << std::endl; + ++index; + } + } + private: std::size_t num_flags{}; std::vector