comparison cpp/options/options.hpp @ 657:11fa64b69530

options: reintroduce a very basic getopt(3) alternative
author David Demelier <markand@malikania.fr>
date Mon, 15 Jul 2019 13:54:17 +0200
parents
children 868663a44b5e
comparison
equal deleted inserted replaced
656:734ce3a26a58 657:11fa64b69530
1 /*
2 * options.hpp -- getopt(3) similar interface for C++
3 *
4 * Copyright (c) 2019 David Demelier <markand@malikania.fr>
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #ifndef OPTIONS_HPP
20 #define OPTIONS_HPP
21
22 /**
23 * \file options.hpp
24 * \brief C++ alternative to getopt(3).
25 */
26
27 #include <stdexcept>
28 #include <string>
29 #include <string_view>
30 #include <tuple>
31 #include <unordered_map>
32 #include <vector>
33
34 /**
35 * \brief C++ alternative to getopt(3).
36 */
37 namespace options {
38
39 /**
40 * Store the positional arguments and options.
41 */
42 using pack = std::tuple<
43 std::vector<std::string>,
44 std::unordered_multimap<char, std::string>
45 >;
46
47 /**
48 * Parse a collection of options and arguments.
49 *
50 * This function uses the same format as getopt(3) function, you need specify
51 * each option in the fmt string and add a colon after the option character if
52 * it requires a value.
53 *
54 * If a -- option appears in the argument list, it stops option parsing and all
55 * next tokens are considered arguments even if they start with an hyphen.
56 *
57 * Example of format strings:
58 *
59 * - "abc": are all three boolean options,
60 * - "c:v": v is a boolean option c requires a value.
61 *
62 * Example of invocation:
63 *
64 * - `mycli -v -a`: is similar to `-va` if both 'v' and 'a' are boolean options,
65 * - `mycli -v -- -c`: -c will be a positional argument rather than an option
66 * but '-v' is still an option.
67 *
68 * \tparam InputIt must dereference a string type (literal, std::string_view or
69 * std::string)
70 * \param it the first item
71 * \param end the next item
72 * \param fmt the format string
73 * \return the result
74 */
75 template <typename InputIt>
76 inline auto parse(InputIt it, InputIt end, std::string_view fmt) -> pack
77 {
78 pack result;
79
80 for (; it != end; ++it) {
81 const std::string_view token(*it);
82
83 if (token == "--") {
84 for (++it; it != end; ++it)
85 std::get<0>(result).push_back(std::string(*it));
86 break;
87 }
88
89 if (token.size() > 0 && token[0] != '-') {
90 std::get<0>(result).push_back(std::string(token));
91 continue;
92 }
93
94 const auto sub = it->substr(1);
95
96 for (std::size_t i = 0U; i < sub.size(); ++i) {
97 const auto idx = fmt.find(sub[i]);
98
99 if (idx == std::string_view::npos)
100 throw std::runtime_error("invalid option");
101
102 if (idx + 1U == fmt.size() || fmt[idx + 1] != ':') {
103 std::get<1>(result).emplace(sub[i], "");
104 continue;
105 }
106
107 if (idx + 1U < sub.size()) {
108 std::get<1>(result).emplace(sub[i], std::string(sub.substr(i + 1)));
109 break;
110 }
111
112 if (++it == end || std::string_view(*it).compare(0U, 1U, "-") == 0)
113 throw std::runtime_error("option require a value");
114
115 std::get<1>(result).emplace(sub[i], std::string(*it));
116 }
117 }
118
119 return result;
120 }
121
122 /**
123 * Convenient overload with an initializer_list.
124 *
125 * \tparam StringType must be either a std::string or std::string_view
126 * \param args the arguments
127 * \param fmt the format string
128 * \return the result
129 */
130 inline auto parse(std::initializer_list<std::string_view> args, std::string_view fmt) -> pack
131 {
132 return parse(args.begin(), args.end(), fmt);
133 }
134
135 /**
136 * Convenient overload for main() arguments.
137 *
138 * \param argc the number of arguments
139 * \param argv the arguments
140 * \param fmt the format string
141 * \return the result
142 */
143 inline auto parse(int argc, char** argv, std::string_view fmt) -> pack
144 {
145 std::vector<std::string_view> args(argc);
146
147 for (int i = 0; i < argc; ++i)
148 args[i] = argv[i];
149
150 return parse(args.begin(), args.end(), fmt);
151 }
152
153
154 } // !options
155
156 #endif // !OPTIONS_HPP