Mercurial > code
changeset 598:ce684e9e2151
Options: use Boost.ProgramOptions instead
author | David Demelier <markand@malikania.fr> |
---|---|
date | Fri, 02 Dec 2016 22:20:10 +0100 |
parents | 2a0b3a7363f2 |
children | 9016afda8527 |
files | CMakeLists.txt modules/options/CMakeLists.txt modules/options/options.cpp modules/options/options.hpp modules/options/test/main.cpp |
diffstat | 5 files changed, 0 insertions(+), 678 deletions(-) [+] |
line wrap: on
line diff
--- a/CMakeLists.txt Fri Dec 02 22:19:22 2016 +0100 +++ b/CMakeLists.txt Fri Dec 02 22:20:10 2016 +0100 @@ -52,7 +52,6 @@ add_subdirectory(modules/elapsed-timer) add_subdirectory(modules/js) add_subdirectory(modules/net) -add_subdirectory(modules/options) add_subdirectory(modules/signals) add_subdirectory(modules/timer) add_subdirectory(modules/unicode)
--- a/modules/options/CMakeLists.txt Fri Dec 02 22:19:22 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ -# -# CMakeLists.txt -- options module -# -# Copyright (c) 2013-2015 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. -# - -project(options) - -code_define_module( - NAME options - SOURCES - ${options_SOURCE_DIR}/options.cpp - ${options_SOURCE_DIR}/options.hpp -)
--- a/modules/options/options.cpp Fri Dec 02 22:19:22 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,179 +0,0 @@ -/* - * options.cpp -- parse Unix command line options - * - * Copyright (c) 2015 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 <cassert> - -#include "options.hpp" - -namespace option { - -namespace { - -using Iterator = std::vector<std::string>::iterator; -using Args = std::vector<std::string>; - -inline bool isOption(const std::string &arg) noexcept -{ - return arg.size() >= 2 && arg[0] == '-'; -} - -inline bool isLongOption(const std::string &arg) noexcept -{ - assert(isOption(arg)); - - return arg.size() >= 3 && arg[1] == '-'; -} - -inline bool isShortSimple(const std::string &arg) noexcept -{ - assert(isOption(arg)); - assert(!isLongOption(arg)); - - return arg.size() == 2; -} - -void parseLongOption(Result &result, Args &args, Iterator &it, Iterator &end, const Options &definition) -{ - auto arg = *it++; - auto opt = definition.find(arg); - - if (opt == definition.end()) - throw InvalidOption{arg}; - - // Need argument? - if (opt->second) { - if (it == end || isOption(*it)) - throw MissingValue{arg}; - - result.insert(std::make_pair(arg, *it++)); - it = args.erase(args.begin(), it); - end = args.end(); - } else { - result.insert(std::make_pair(arg, "")); - it = args.erase(args.begin()); - end = args.end(); - } -} - -void parseShortOption(Result &result, Args &args, Iterator &it, Iterator &end, const Options &definition) -{ - if (isShortSimple(*it)) { - /* - * Here two cases: - * - * -v (no option) - * -c value - */ - auto arg = *it++; - auto opt = definition.find(arg); - - if (opt == definition.end()) - throw InvalidOption{arg}; - - // Need argument? - if (opt->second) { - if (it == end || isOption(*it)) - throw MissingValue{arg}; - - result.insert(std::make_pair(arg, *it++)); - it = args.erase(args.begin(), it); - end = args.end(); - } else { - result.insert(std::make_pair(arg, "")); - it = args.erase(args.begin()); - end = args.end(); - } - } else { - /* - * Here multiple scenarios: - * - * 1. -abc (-a -b -c if all are simple boolean arguments) - * 2. -vc foo.conf (-v -c foo.conf if -c is argument dependant) - * 3. -vcfoo.conf (-v -c foo.conf also) - */ - auto value = it->substr(1); - auto len = value.length(); - int toremove = 1; - - for (decltype(len) i = 0; i < len; ++i) { - auto arg = std::string{'-'} + value[i]; - auto opt = definition.find(arg); - - if (opt == definition.end()) - throw InvalidOption{arg}; - - if (opt->second) { - if (i == (len - 1)) { - // End of string, get the next argument (see 2.). - if (++it == end || isOption(*it)) - throw MissingValue{arg}; - - result.insert(std::make_pair(arg, *it)); - toremove += 1; - } else { - result.insert(std::make_pair(arg, value.substr(i + 1))); - i = len; - } - } else - result.insert(std::make_pair(arg, "")); - } - - it = args.erase(args.begin(), args.begin() + toremove); - end = args.end(); - } -} - -} // !namespace - -Result read(std::vector<std::string> &args, const Options &definition) -{ - Result result; - - auto it = args.begin(); - auto end = args.end(); - - while (it != end) { - if (!isOption(*it)) - break; - - if (isLongOption(*it)) - parseLongOption(result, args, it, end, definition); - else - parseShortOption(result, args, it, end, definition); - } - - return result; -} - -Result read(int &argc, char **&argv, const Options &definition) -{ - std::vector<std::string> args; - - for (int i = 0; i < argc; ++i) - args.push_back(argv[i]); - - auto before = args.size(); - auto result = read(args, definition); - - argc -= before - args.size(); - argv += before - args.size(); - - return result; -} - -} // !option
--- a/modules/options/options.hpp Fri Dec 02 22:19:22 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,185 +0,0 @@ -/* - * options.hpp -- parse Unix command line options - * - * Copyright (c) 2015 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 OPTIONS_HPP -#define OPTIONS_HPP - -/** - * \file options.hpp - * \brief Basic Unix options parser. - */ - -/** - * \page options Options parser. - * - * ## Export macros - * - * You must define `OPTIONS_DLL` globally and `OPTIONS_BUILDING_DLL` when - * compiling the library if you want a DLL, alternatively you can provide your - * own `OPTIONS_EXPORT` macro instead. - */ - -/** - * \cond OPTIONS_HIDDEN_SYMBOLS - */ - -#if !defined(OPTIONS_EXPORT) -# if defined(OPTIONS_DLL) -# if defined(_WIN32) -# if defined(OPTIONS_BUILDING_DLL) -# define OPTIONS_EXPORT __declspec(dllexport) -# else -# define OPTIONS_EXPORT __declspec(dllimport) -# endif -# else -# define OPTIONS_EXPORT -# endif -# else -# define OPTIONS_EXPORT -# endif -#endif - -/** - * \endcond - */ - -#include <exception> -#include <map> -#include <string> -#include <utility> -#include <vector> - -/** - * Namespace for options parsing. - */ -namespace option { - -/** - * \brief This exception is thrown when an invalid option has been found. - */ -class InvalidOption : public std::exception { -private: - std::string message; - -public: - /** - * The invalid option given. - */ - std::string argument; - - /** - * Construct the exception. - * - * \param arg the argument missing - */ - inline InvalidOption(std::string arg) - : argument(std::move(arg)) - { - message = std::string("invalid option: ") + argument; - } - - /** - * Get the error message. - * - * \return the error message - */ - const char *what() const noexcept override - { - return message.c_str(); - } -}; - -/** - * \brief This exception is thrown when an option requires a value and no value - * has been given. - */ -class MissingValue : public std::exception { -private: - std::string m_message; - std::string m_option; - -public: - /** - * Construct the exception. - * - * \param option the option that requires a value - */ - inline MissingValue(std::string option) - : m_option(std::move(option)) - { - m_message = std::string("missing argument for: ") + m_option; - } - - /** - * Get the options that requires a value. - * - * \return the option name - */ - inline const std::string &option() const noexcept - { - return m_option; - } - - /** - * Get the error message. - * - * \return the error message - */ - const char *what() const noexcept override - { - return m_message.c_str(); - } -}; - -/** - * Packed multimap of options. - */ -using Result = std::multimap<std::string, std::string>; - -/** - * Define the allowed options. - */ -using Options = std::map<std::string, bool>; - -/** - * Extract the command line options and return a result. - * - * \param args the arguments - * \param definition - * \warning the arguments vector is modified in place to remove parsed options - * \throw MissingValue - * \throw InvalidOption - */ -OPTIONS_EXPORT Result read(std::vector<std::string> &args, const Options &definition); - -/** - * Overloaded function for usage with main() arguments. - * - * \param argc the number of arguments - * \param argv the argument vector - * \param definition - * \note don't forget to remove the first argv[0] argument - * \warning the argc and argv are modified in place to remove parsed options - * \throw MissingValue - * \throw InvalidOption - */ -OPTIONS_EXPORT Result read(int &argc, char **&argv, const Options &definition); - -} // !option - -#endif // !OPTIONS_HPP
--- a/modules/options/test/main.cpp Fri Dec 02 22:19:22 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,287 +0,0 @@ -/* - * main.cpp -- main test file for OptionParser - * - * Copyright (c) 2013-2015 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 <gtest/gtest.h> - -#include <options.hpp> - -/* - * Short options. - * ------------------------------------------------------------------ - */ - -TEST(Short, simpleNoArg) -{ - std::vector<std::string> args{"-a", "-b"}; - - option::Options options{ - { "-a", false }, - { "-b", false } - }; - - option::Result pack = option::read(args, options); - - ASSERT_EQ(2U, pack.size()); - ASSERT_EQ(0U, args.size()); - - ASSERT_TRUE(pack.count("-a") != 0); - ASSERT_TRUE(pack.count("-b") != 0); -} - -TEST(Short, simpleArg) -{ - std::vector<std::string> args{"-v", "-cfoo.conf"}; - - option::Options options{ - { "-v", false }, - { "-c", true } - }; - - option::Result pack = option::read(args, options); - - ASSERT_EQ(2U, pack.size()); - ASSERT_EQ(0U, args.size()); - - ASSERT_TRUE(pack.count("-v") != 0); - ASSERT_EQ("foo.conf", pack.find("-c")->second); -} - -TEST(Short, spacedArg) -{ - std::vector<std::string> args{"-v", "-c", "foo.conf"}; - - option::Options options{ - { "-v", false }, - { "-c", true } - }; - - option::Result pack = option::read(args, options); - - ASSERT_EQ(2U, pack.size()); - ASSERT_EQ(0U, args.size()); - - ASSERT_TRUE(pack.count("-v") != 0); - ASSERT_EQ("foo.conf", pack.find("-c")->second); -} - -TEST(Short, compacted) -{ - std::vector<std::string> args{"-abc"}; - - option::Options options{ - { "-a", false }, - { "-b", false }, - { "-c", false }, - }; - - option::Result pack = option::read(args, options); - - ASSERT_EQ(3U, pack.size()); - ASSERT_EQ(0U, args.size()); - - ASSERT_TRUE(pack.count("-a") != 0); - ASSERT_TRUE(pack.count("-b") != 0); - ASSERT_TRUE(pack.count("-c") != 0); -} - -TEST(Short, compactedArg) -{ - std::vector<std::string> args{"-vdcfoo.conf"}; - - option::Options options{ - { "-v", false }, - { "-d", false }, - { "-c", true }, - }; - - option::Result pack = option::read(args, options); - - ASSERT_EQ(3U, pack.size()); - ASSERT_EQ(0U, args.size()); - - ASSERT_TRUE(pack.count("-v") != 0); - ASSERT_TRUE(pack.count("-d") != 0); - ASSERT_EQ("foo.conf", pack.find("-c")->second); -} - -/* - * Long options. - * ------------------------------------------------------------------ - */ - -TEST(Long, simple) -{ - std::vector<std::string> args{"--fullscreen"}; - - option::Options options{ - { "--verbose", false }, - { "--fullscreen", false } - }; - - option::Result pack = option::read(args, options); - - ASSERT_EQ(1U, pack.size()); - ASSERT_EQ(0U, args.size()); - - ASSERT_TRUE(pack.count("--fullscreen") != 0); -} - -TEST(Long, simpleArg) -{ - std::vector<std::string> args{"--config", "config.conf", "--level", "2"}; - - option::Options options{ - { "--config", true }, - { "--level", true } - }; - - option::Result pack = option::read(args, options); - - ASSERT_EQ(2U, pack.size()); - ASSERT_EQ(0U, args.size()); - - ASSERT_EQ("config.conf", pack.find("--config")->second); - ASSERT_EQ("2", pack.find("--level")->second); -} - -/* - * Errors. - * ------------------------------------------------------------------ - */ - -TEST(Errors, stop) -{ - std::vector<std::string> args{"-v", "install", "-y", "irccd"}; - std::vector<std::string> expected{"install", "-y", "irccd"}; - - option::Options options{ - { "-v", false } - }; - - option::Result pack = option::read(args, options); - - ASSERT_EQ(1U, pack.size()); - ASSERT_EQ(3U, args.size()); - - ASSERT_TRUE(pack.count("-v") != 0); - ASSERT_EQ(expected, args); -} - -TEST(Errors, missingShortArg) -{ - std::vector<std::string> args{"-c"}; - - option::Options options{ - { "-c", true } - }; - - try { - option::Result pack = option::read(args, options); - - FAIL() << "exception expected"; - } catch (const option::MissingValue &) { - } -} - -TEST(Errors, missingShortArg2) -{ - std::vector<std::string> args{"-vc"}; - - option::Options options{ - { "-v", false }, - { "-c", true } - }; - - try { - option::Result pack = option::read(args, options); - - FAIL() << "exception expected"; - } catch (const option::MissingValue &) { - } -} - -TEST(Errors, missingLongArg) -{ - std::vector<std::string> args{"--config"}; - - option::Options options{ - { "--config", true } - }; - - try { - option::Result pack = option::read(args, options); - - FAIL() << "exception expected"; - } catch (const option::MissingValue &) { - } -} - -TEST(Errors, invalidOption) -{ - std::vector<std::string> args{"-x"}; - - option::Options options{ - { "-v", true } - }; - - try { - option::Result pack = option::read(args, options); - - FAIL() << "exception expected"; - } catch (const option::InvalidOption &) { - } -} - -TEST(Errors, invalidOption2) -{ - std::vector<std::string> args{"--destroy"}; - - option::Options options{ - { "--verbose", true } - }; - - try { - option::Result pack = option::read(args, options); - - FAIL() << "exception expected"; - } catch (const option::InvalidOption &) { - } -} - -TEST(Errors, invalidOption3) -{ - std::vector<std::string> args{"-vx"}; - - option::Options options{ - { "-x", true } - }; - - try { - option::Result pack = option::read(args, options); - - FAIL() << "exception expected"; - } catch (const option::InvalidOption &) { - } -} - -int main(int argc, char **argv) -{ - testing::InitGoogleTest(&argc, argv); - - return RUN_ALL_TESTS(); -}