view C++/OptionParser.h @ 232:420b1710355f

Luae: disable optionals, it is not currently ready
author David Demelier <markand@malikania.fr>
date Sun, 22 Jun 2014 16:23:15 +0200
parents 96ff112d05cf
children ff2db0ed78f1
line wrap: on
line source

/*
 * 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_