#pragma once #include #include #include #include #include #include #include #include #include namespace dg { using Handle = std::function; class Options { public: /// @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, 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, std::string const & help_text, Handle handle) { names.push_back(name); // create a new option from the args options.push_back({names.back().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) { // flag is not null, so the val is not used as identifier // so pick another one index = num_flags++; } // store the handle handles[index] = handle; } int process(int argc, char* argv[]) { int ret = 0; std::string shortopts; for (auto const & option: options) { if (option.flag != nullptr) { continue; } shortopts += static_cast(option.val); switch (option.has_arg) { case no_argument: break; case required_argument: shortopts += ":"; break; case optional_argument: shortopts += "::"; break; } } // add termination option options.push_back({0, 0, 0, 0}); // handle arguments while (true) { int index = 0; int key = getopt_long(argc, argv, shortopts.data(), options.data(), &index); if (key == -1) { break; } else if (key == '?') { return 1; } else if (key == ':') { return 1; } else if (key == 0) { // call flag's handle ret = handles.at(index)(); } else { // call option's handle ret = handles.at(key)(); } if(ret) { return ret; } } for (int i = optind; i < argc; ++i) { args.push_back(argv[i]); } // 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 = 26; std::size_t column_width = 80; int index = 0; for(auto const & help_text : help_texts) { if(index >= (int)options.size()) { break; } 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 option_str; if(opt.val >= '!' && opt.val <= '~') { option_str = " -" + std::string(1, opt.val) + ", --" + opt.name + " " + args; } else { option_str = " --" + std::string(opt.name) + " " + args; } std::string padding; if(option_str.size() < width) { padding.append(width - option_str.size(), ' '); } else { padding = "\n"; padding.append(width, ' '); } std::cout << option_str << padding; auto i = width; for(auto c : help_text) { if((c == '\n') || (i > column_width && (c == ' ' || c == '\t'))) { std::string padding(width, ' '); std::cout << '\n' << padding; i = width; continue; } std::cout << c; ++i; } std::cout << '\n'; ++index; } } private: std::size_t num_flags{}; std::vector