Mercurial > code
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 |