Mercurial > code
changeset 231:2f1d820e6e33
Merge with OptionParser
author | David Demelier <markand@malikania.fr> |
---|---|
date | Sun, 22 Jun 2014 16:19:31 +0200 |
parents | 5e1001649d07 (current diff) 96ff112d05cf (diff) |
children | 420b1710355f |
files | |
diffstat | 2 files changed, 556 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/OptionParser.cpp Sun Jun 22 16:19:31 2014 +0200 @@ -0,0 +1,228 @@ +/* + * OptionParser.cpp -- safe getopt(3) replacement + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <iostream> +#include <iomanip> + +#include "OptionParser.h" + +/* -------------------------------------------------------- + * Option class + * -------------------------------------------------------- */ + +Option::Option(const std::string &name, const std::string &help, Type type, int flags) + : m_name(name) + , m_help(help) + , m_type(type) + , m_flags(flags) +{ +} + +const std::string &Option::name() const +{ + return m_name; +} + +const std::string &Option::help() const +{ + return m_help; +} + +Option::Type Option::type() const +{ + return m_type; +} + +int Option::flags() const +{ + return m_flags; +} + +/* -------------------------------------------------------- + * OptionValue class + * -------------------------------------------------------- */ + +OptionValue::OptionValue(const std::string &name) + : m_name(name) + , m_enabled(true) +{ +} + +OptionValue::OptionValue(const std::string &name, const std::string &value) + : m_name(name) + , m_value(value) +{ +} + +const std::string &OptionValue::name() const +{ + return m_name; +} + +const std::string &OptionValue::value() const +{ + return m_value; +} + +OptionValue::operator bool() const +{ + return m_enabled; +} + +OptionValue::operator std::string() const +{ + return m_value; +} + +/* -------------------------------------------------------- + * OptionResult class + * -------------------------------------------------------- */ + +auto OptionResult::begin() -> decltype(m_values.begin()) +{ + return m_values.begin(); +} + +auto OptionResult::begin() const -> decltype(m_values.begin()) +{ + return m_values.begin(); +} + +auto OptionResult::cbegin() const -> decltype(m_values.cbegin()) +{ + return m_values.begin(); +} + +auto OptionResult::end() -> decltype(m_values.end()) +{ + return m_values.end(); +} + +auto OptionResult::end() const -> decltype(m_values.end()) +{ + return m_values.end(); +} + +auto OptionResult::cend() const -> decltype(m_values.cend()) +{ + return m_values.cend(); +} + +int OptionResult::count() const +{ + return static_cast<int>(m_values.size()); +} + +int OptionResult::total() const +{ + return m_total; +} + +/* -------------------------------------------------------- + * OptionParser class + * -------------------------------------------------------- */ + +void OptionParser::add(Option &&option) +{ + const auto &name = option.name(); + + if (name.length() > m_maxlength) + m_maxlength = name.length(); + + m_options.emplace(std::make_pair(name, std::move(option))); +} + +void OptionParser::add(const Option &option) +{ + add(const_cast<Option &>(option)); +} + +OptionResult OptionParser::parse(int argc, char **argv, int flags) +{ + OptionResult result; + int i; + + for (i = 0; i < argc; ++i) { + /* Not a valid option at all? */ + if (m_options.count(argv[i]) == 0) { + log(argv[i], std::string(argv[i]) + " is not a valid option"); + + if (flags & Strict) { + usage(); + break; + } else + continue; + } + + /* + * At this step, we have an option name valid, check if it requires + * an argument or not. + */ + const auto &option = m_options.at(argv[i]); + const auto &name = option.name(); + auto type = option.type(); + + /* + * Already present and must appears only once? + */ + if (option.flags() & Option::Single && m_placed.count(name) > 0) { + if (!(flags & Silent)) { + log(name, "option " + name + " must appear only once"); + + if (flags & Strict) { + usage(); + break; + } + } else if (flags & Strict) + break; + } + + /* + * Need an argument or it is a simple switch ? + */ + if (type == Option::Type::Switch) + result.m_values.push_back(OptionValue(name)); + else { + if (i + 1 >= argc) + result.m_values.push_back(OptionValue(name, "")); + else { + result.m_values.push_back(OptionValue(name, argv[i + 1])); + ++ i; + } + } + + m_placed.insert(name); + } + + result.m_total = i; + + return result; +} + +void OptionParser::usage() +{ + std::cout << "options available: " << std::endl; + + for (const auto &option : m_options) + std::cout << " " << std::setw(m_maxlength) << option.second.name() << "\t" << option.second.help() << std::endl; +} + +void OptionParser::log(const std::string &, const std::string &error) +{ + std::cout << error << std::endl; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/OptionParser.h Sun Jun 22 16:19:31 2014 +0200 @@ -0,0 +1,328 @@ +/* + * OptionParser.h -- safe getopt(3) replacement + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _OPTION_PARSER_H_ +#define _OPTION_PARSER_H_ + +/** + * @file OptionParser.h + * @brief Command line option replacement for getopt(3) + * + * This alternative to getopt is thread safe, reentrant and stateless. It does + * not use any global and is customizable. + */ + +#include <string> +#include <vector> +#include <map> +#include <unordered_set> + +/** + * @class Option + * @brief Option description + * + * This class describe an option with its optional flags. Name can be arbitrary + * and must specify by hand - or --. + */ +class Option final { +public: + enum class Type { + Switch, + Argument + }; + + /** + * @brief Optional flags + */ + enum { + Multiple = 0, //!< can appear multiple times + Single //!< must appear exactly once + }; + +private: + std::string m_name; + std::string m_help; + int m_flags; + Type m_type; + +public: + /** + * Build an option. + * + * @param name the name + * @param help the help message + * @param flags the flags + */ + Option(const std::string &name, + const std::string &help, + Type type = Type::Switch, + int flags = 0); + + /** + * Get the option name. + * + * @return the name + */ + const std::string &name() const; + + /** + * Get the help. + * + * @return the help + */ + const std::string &help() const; + + /** + * Get the option type. + * + * @return the type + */ + Type type() const; + + /** + * Get the flags. + * + * @return the flags + */ + int flags() const; +}; + +/** + * @class OptionValue + * @brief Describe the result of an option + * + * This class is instanciated when an option has been declared and successfully parsed. + * + * One should use the operator bool for boolean options and operator std::string (or value()) + * for options with arguments. + */ +class OptionValue final { +private: + friend class OptionParser; + + std::string m_name; + std::string m_value; + bool m_enabled = false; + +private: + /** + * Construct an option value. This is constructor is used + * with Option::Switch. + * + * @param name the option name + */ + OptionValue(const std::string &name); + + /** + * Construct a value with a parameter. + * + * @param name the name + * @param value the value + */ + OptionValue(const std::string &name, const std::string &value); + +public: + /** + * Get the option name. + * + * @return the name + */ + const std::string &name() const; + + /** + * Get the value. + * + * @return the value + */ + const std::string &value() const; + + /** + * Check if the option is enabled (only with Option::Switch) + * + * @return true if enabled + */ + operator bool() const; + + /** + * Convert the value to string. + * + * @return the value + */ + operator std::string() const; +}; + +/** + * @class OptionResult + * @brief Result of a command line parse + */ +class OptionResult final { +private: + friend class OptionParser; + + using Values = std::vector<OptionValue>; + + Values m_values; + int m_total = 0; + +public: + /** + * Default constructor. + */ + OptionResult() = default; + + /** + * Move constructor. + * + * @param other the other + */ + OptionResult(OptionResult &&other) = default; + + /** + * Returns an iterator to the beginning of values. + * + * @return the iterator + */ + auto begin() -> decltype(m_values.begin()); + + /** + * Returns a const iterator to the beginning of values. + * + * @return the iterator + */ + auto begin() const -> decltype(m_values.begin()); + + /** + * Returns a const iterator to the beginning of values. + * + * @return the iterator + */ + auto cbegin() const -> decltype(m_values.cbegin()); + + /** + * Returns an iterator to the end of values. + * + * @return the iterator + */ + auto end() -> decltype(m_values.end()); + + /** + * Returns a const iterator to the end of values. + * + * @return the iterator + */ + auto end() const -> decltype(m_values.end()); + + /** + * Returns a const iterator to the end of values. + * + * @return the iterator + */ + auto cend() const -> decltype(m_values.cend()); + + /** + * Count the number of parsed options. + * + * @return the number + */ + int count() const; + + /** + * Get the total number of tokens parsed from the argv vector. + * + * @return the total number + */ + int total() const; + + /** + * Move assignment operator. + * + * @param other the other + * @return the result + */ + OptionResult &operator=(OptionResult &&other) = default; +}; + +/** + * @class OptionParser + * @brief Parse command line options + */ +class OptionParser { +private: + using Placed = std::unordered_set<std::string>; + +public: + using Options = std::map<std::string, Option>; + + enum { + BreakNonOption = (1 << 0), //!< break on first non option + Strict = (1 << 1), //!< abort on first error + Silent = (1 << 2) //!< do not / call any log handler + }; + +private: + Placed m_placed; //!< already placed options + +protected: + Options m_options; //!< list of options descriptions + int m_maxlength = 0; //!< max option length + +public: + /** + * Default destructor. + */ + virtual ~OptionParser() = default; + + /** + * Move an option. + * + * @param option the option + */ + void add(Option &&option); + + /** + * Add an option. + * + * @param option the option + */ + void add(const Option &option); + + /** + * Parse options. + * + * @param argc the number of args + * @param argv the arguments + * @param flags the optional flags + */ + OptionResult parse(int argc, char **argv, int flags = 0); + + /** + * Show the usage. Called on error only if silent and if is strict is set, because + * it will continue parsing and may show usage multiple times. + */ + virtual void usage(); + + /** + * On error log. + * + * @param option the option + * @param error the error + */ + virtual void log(const std::string &option, const std::string &error); +}; + +#endif // !_OPTION_PARSER_H_