midas-edge 0.1.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -0
- data/NOTICE.txt +2 -1
- data/README.md +8 -7
- data/ext/midas/ext.cpp +45 -32
- data/ext/midas/extconf.rb +1 -5
- data/lib/midas/detector.rb +7 -4
- data/lib/midas/version.rb +1 -1
- data/vendor/MIDAS/LICENSE +0 -25
- data/vendor/MIDAS/README.md +185 -40
- data/vendor/MIDAS/src/CountMinSketch.hpp +105 -0
- data/vendor/MIDAS/src/FilteringCore.hpp +98 -0
- data/vendor/MIDAS/src/NormalCore.hpp +53 -0
- data/vendor/MIDAS/src/RelationalCore.hpp +79 -0
- metadata +15 -75
- data/vendor/MIDAS/anom.cpp +0 -88
- data/vendor/MIDAS/anom.hpp +0 -10
- data/vendor/MIDAS/argparse.hpp +0 -539
- data/vendor/MIDAS/edgehash.cpp +0 -63
- data/vendor/MIDAS/edgehash.hpp +0 -25
- data/vendor/MIDAS/main.cpp +0 -127
- data/vendor/MIDAS/nodehash.cpp +0 -63
- data/vendor/MIDAS/nodehash.hpp +0 -25
data/vendor/MIDAS/anom.cpp
DELETED
@@ -1,88 +0,0 @@
|
|
1
|
-
#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
|
2
|
-
#define MAX(X, Y) (((X) > (Y)) ? (X) : (Y))
|
3
|
-
|
4
|
-
#include <iostream>
|
5
|
-
#include <math.h>
|
6
|
-
#include <algorithm>
|
7
|
-
#include <vector>
|
8
|
-
#include "anom.hpp"
|
9
|
-
#include "edgehash.hpp"
|
10
|
-
#include "nodehash.hpp"
|
11
|
-
|
12
|
-
vector<double>* midas(vector<int>& src, vector<int>& dst, vector<int>& times, int num_rows, int num_buckets)
|
13
|
-
{
|
14
|
-
int m = *max_element(src.begin(), src.end());
|
15
|
-
Edgehash cur_count(num_rows, num_buckets, m);
|
16
|
-
Edgehash total_count(num_rows, num_buckets, m);
|
17
|
-
vector<double>* anom_score = new vector<double>(src.size());
|
18
|
-
int cur_t = 1, size = src.size(), cur_src, cur_dst;
|
19
|
-
double cur_mean, sqerr, cur_score;
|
20
|
-
for (int i = 0; i < size; i++) {
|
21
|
-
|
22
|
-
if (i == 0 || times[i] > cur_t) {
|
23
|
-
cur_count.clear();
|
24
|
-
cur_t = times[i];
|
25
|
-
}
|
26
|
-
|
27
|
-
cur_src = src[i];
|
28
|
-
cur_dst = dst[i];
|
29
|
-
cur_count.insert(cur_src, cur_dst, 1);
|
30
|
-
total_count.insert(cur_src, cur_dst, 1);
|
31
|
-
cur_mean = total_count.get_count(cur_src, cur_dst) / cur_t;
|
32
|
-
sqerr = pow(cur_count.get_count(cur_src, cur_dst) - cur_mean, 2);
|
33
|
-
if (cur_t == 1) cur_score = 0;
|
34
|
-
else cur_score = sqerr / cur_mean + sqerr / (cur_mean * (cur_t - 1));
|
35
|
-
(*anom_score)[i] = cur_score;
|
36
|
-
}
|
37
|
-
|
38
|
-
return anom_score;
|
39
|
-
}
|
40
|
-
|
41
|
-
double counts_to_anom(double tot, double cur, int cur_t)
|
42
|
-
{
|
43
|
-
double cur_mean = tot / cur_t;
|
44
|
-
double sqerr = pow(MAX(0, cur - cur_mean), 2);
|
45
|
-
return sqerr / cur_mean + sqerr / (cur_mean * MAX(1, cur_t - 1));
|
46
|
-
}
|
47
|
-
|
48
|
-
vector<double>* midasR(vector<int>& src, vector<int>& dst, vector<int>& times, int num_rows, int num_buckets, double factor)
|
49
|
-
{
|
50
|
-
int m = *max_element(src.begin(), src.end());
|
51
|
-
Edgehash cur_count(num_rows, num_buckets, m);
|
52
|
-
Edgehash total_count(num_rows, num_buckets, m);
|
53
|
-
Nodehash src_score(num_rows, num_buckets);
|
54
|
-
Nodehash dst_score(num_rows, num_buckets);
|
55
|
-
Nodehash src_total(num_rows, num_buckets);
|
56
|
-
Nodehash dst_total(num_rows, num_buckets);
|
57
|
-
vector<double>* anom_score = new vector<double>(src.size());
|
58
|
-
int cur_t = 1, size = src.size(), cur_src, cur_dst;
|
59
|
-
double cur_score, cur_score_src, cur_score_dst, combined_score;
|
60
|
-
|
61
|
-
for (int i = 0; i < size; i++) {
|
62
|
-
|
63
|
-
if (i == 0 || times[i] > cur_t) {
|
64
|
-
cur_count.lower(factor);
|
65
|
-
src_score.lower(factor);
|
66
|
-
dst_score.lower(factor);
|
67
|
-
cur_t = times[i];
|
68
|
-
}
|
69
|
-
|
70
|
-
cur_src = src[i];
|
71
|
-
cur_dst = dst[i];
|
72
|
-
cur_count.insert(cur_src, cur_dst, 1);
|
73
|
-
total_count.insert(cur_src, cur_dst, 1);
|
74
|
-
src_score.insert(cur_src, 1);
|
75
|
-
dst_score.insert(cur_dst, 1);
|
76
|
-
src_total.insert(cur_src, 1);
|
77
|
-
dst_total.insert(cur_dst, 1);
|
78
|
-
cur_score = counts_to_anom(total_count.get_count(cur_src, cur_dst), cur_count.get_count(cur_src, cur_dst), cur_t);
|
79
|
-
cur_score_src = counts_to_anom(src_total.get_count(cur_src), src_score.get_count(cur_src), cur_t);
|
80
|
-
cur_score_dst = counts_to_anom(dst_total.get_count(cur_dst), dst_score.get_count(cur_dst), cur_t);
|
81
|
-
//combined_score = MAX(cur_score_src, cur_score_dst) + cur_score;
|
82
|
-
//combined_score = cur_score_src + cur_score_dst + cur_score;
|
83
|
-
combined_score = MAX(MAX(cur_score_src, cur_score_dst), cur_score);
|
84
|
-
(*anom_score)[i] = log(1 + combined_score);
|
85
|
-
}
|
86
|
-
|
87
|
-
return anom_score;
|
88
|
-
}
|
data/vendor/MIDAS/anom.hpp
DELETED
@@ -1,10 +0,0 @@
|
|
1
|
-
#ifndef anom_hpp
|
2
|
-
#define anom_hpp
|
3
|
-
|
4
|
-
#include <vector>
|
5
|
-
using namespace std;
|
6
|
-
|
7
|
-
vector<double>* midas(vector<int>& src, vector<int>& dst, vector<int>& times, int num_rows, int num_buckets);
|
8
|
-
vector<double>* midasR(vector<int>& src, vector<int>& dst, vector<int>& times, int num_rows, int num_buckets, double factor);
|
9
|
-
|
10
|
-
#endif /* anom_hpp */
|
data/vendor/MIDAS/argparse.hpp
DELETED
@@ -1,539 +0,0 @@
|
|
1
|
-
/*
|
2
|
-
__ _ _ __ __ _ _ __ __ _ _ __ ___ ___
|
3
|
-
/ _` | '__/ _` | '_ \ / _` | '__/ __|/ _ \ Argument Parser for Modern C++
|
4
|
-
| (_| | | | (_| | |_) | (_| | | \__ \ __/ http://github.com/p-ranav/argparse
|
5
|
-
\__,_|_| \__, | .__/ \__,_|_| |___/\___|
|
6
|
-
|___/|_|
|
7
|
-
|
8
|
-
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
9
|
-
SPDX-License-Identifier: MIT
|
10
|
-
Copyright (c) 2019 Pranav Srinivas Kumar <pranav.srinivas.kumar@gmail.com>.
|
11
|
-
|
12
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
13
|
-
of this software and associated documentation files (the "Software"), to deal
|
14
|
-
in the Software without restriction, including without limitation the rights
|
15
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
16
|
-
copies of the Software, and to permit persons to whom the Software is
|
17
|
-
furnished to do so, subject to the following conditions:
|
18
|
-
|
19
|
-
The above copyright notice and this permission notice shall be included in all
|
20
|
-
copies or substantial portions of the Software.
|
21
|
-
|
22
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
23
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
24
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
25
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
26
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
27
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
28
|
-
SOFTWARE.
|
29
|
-
*/
|
30
|
-
#pragma once
|
31
|
-
#include <algorithm>
|
32
|
-
#include <any>
|
33
|
-
#include <functional>
|
34
|
-
#include <iomanip>
|
35
|
-
#include <iostream>
|
36
|
-
#include <iterator>
|
37
|
-
#include <list>
|
38
|
-
#include <map>
|
39
|
-
#include <memory>
|
40
|
-
#include <numeric>
|
41
|
-
#include <sstream>
|
42
|
-
#include <stdexcept>
|
43
|
-
#include <string>
|
44
|
-
#include <type_traits>
|
45
|
-
#include <vector>
|
46
|
-
|
47
|
-
namespace argparse {
|
48
|
-
|
49
|
-
namespace details { // namespace for helper methods
|
50
|
-
|
51
|
-
template <typename... Ts> struct is_container_helper {};
|
52
|
-
|
53
|
-
template <typename T, typename _ = void>
|
54
|
-
struct is_container : std::false_type {};
|
55
|
-
|
56
|
-
template <> struct is_container<std::string> : std::false_type {};
|
57
|
-
|
58
|
-
template <typename T>
|
59
|
-
struct is_container<
|
60
|
-
T,
|
61
|
-
std::conditional_t<false,
|
62
|
-
is_container_helper<typename T::value_type,
|
63
|
-
decltype(std::declval<T>().begin()),
|
64
|
-
decltype(std::declval<T>().end()),
|
65
|
-
decltype(std::declval<T>().size())>,
|
66
|
-
void>> : public std::true_type {};
|
67
|
-
|
68
|
-
template <typename T>
|
69
|
-
static constexpr bool is_container_v = is_container<T>::value;
|
70
|
-
|
71
|
-
template <typename T>
|
72
|
-
using enable_if_container = std::enable_if_t<is_container_v<T>, T>;
|
73
|
-
|
74
|
-
template <typename T>
|
75
|
-
using enable_if_not_container = std::enable_if_t<!is_container_v<T>, T>;
|
76
|
-
} // namespace
|
77
|
-
|
78
|
-
class Argument {
|
79
|
-
friend class ArgumentParser;
|
80
|
-
|
81
|
-
public:
|
82
|
-
Argument() = default;
|
83
|
-
|
84
|
-
template <typename... Args>
|
85
|
-
explicit Argument(Args... args)
|
86
|
-
: mNames({std::move(args)...}), mIsOptional((is_optional(args) || ...)) {
|
87
|
-
std::sort(
|
88
|
-
mNames.begin(), mNames.end(), [](const auto &lhs, const auto &rhs) {
|
89
|
-
return lhs.size() == rhs.size() ? lhs < rhs : lhs.size() < rhs.size();
|
90
|
-
});
|
91
|
-
}
|
92
|
-
|
93
|
-
Argument &help(std::string aHelp) {
|
94
|
-
mHelp = std::move(aHelp);
|
95
|
-
return *this;
|
96
|
-
}
|
97
|
-
|
98
|
-
Argument &default_value(std::any aDefaultValue) {
|
99
|
-
mDefaultValue = std::move(aDefaultValue);
|
100
|
-
return *this;
|
101
|
-
}
|
102
|
-
|
103
|
-
Argument &required() {
|
104
|
-
mIsRequired = true;
|
105
|
-
return *this;
|
106
|
-
}
|
107
|
-
|
108
|
-
Argument &implicit_value(std::any aImplicitValue) {
|
109
|
-
mImplicitValue = std::move(aImplicitValue);
|
110
|
-
mNumArgs = 0;
|
111
|
-
return *this;
|
112
|
-
}
|
113
|
-
|
114
|
-
Argument &action(std::function<std::any(const std::string &)> aAction) {
|
115
|
-
mAction = std::move(aAction);
|
116
|
-
return *this;
|
117
|
-
}
|
118
|
-
|
119
|
-
Argument &nargs(size_t aNumArgs) {
|
120
|
-
mNumArgs = aNumArgs;
|
121
|
-
return *this;
|
122
|
-
}
|
123
|
-
|
124
|
-
template <typename Iterator>
|
125
|
-
Iterator consume(Iterator start, Iterator end, std::string usedName = {}) {
|
126
|
-
if (mIsUsed) {
|
127
|
-
throw std::runtime_error("Duplicate argument");
|
128
|
-
}
|
129
|
-
mIsUsed = true;
|
130
|
-
mUsedName = std::move(usedName);
|
131
|
-
if (mNumArgs == 0) {
|
132
|
-
mValues.emplace_back(mImplicitValue);
|
133
|
-
return start;
|
134
|
-
} else if (mNumArgs <= static_cast<size_t>(std::distance(start, end))) {
|
135
|
-
end = std::next(start, mNumArgs);
|
136
|
-
if (std::any_of(start, end, Argument::is_optional)) {
|
137
|
-
throw std::runtime_error("optional argument in parameter sequence");
|
138
|
-
}
|
139
|
-
std::transform(start, end, std::back_inserter(mValues), mAction);
|
140
|
-
return end;
|
141
|
-
} else if (mDefaultValue.has_value()) {
|
142
|
-
return start;
|
143
|
-
} else {
|
144
|
-
throw std::runtime_error("Too few arguments");
|
145
|
-
}
|
146
|
-
}
|
147
|
-
|
148
|
-
/*
|
149
|
-
* @throws std::runtime_error if argument values are not valid
|
150
|
-
*/
|
151
|
-
void validate() const {
|
152
|
-
if (mIsOptional) {
|
153
|
-
if (mIsUsed && mValues.size() != mNumArgs && !mDefaultValue.has_value()) {
|
154
|
-
std::stringstream stream;
|
155
|
-
stream << mUsedName << ": expected " << mNumArgs
|
156
|
-
<< " argument(s). " << mValues.size() << " provided.";
|
157
|
-
throw std::runtime_error(stream.str());
|
158
|
-
} else {
|
159
|
-
// TODO: check if an implicit value was programmed for this argument
|
160
|
-
if (!mIsUsed && !mDefaultValue.has_value() && mIsRequired) {
|
161
|
-
std::stringstream stream;
|
162
|
-
stream << mNames[0] << ": required.";
|
163
|
-
throw std::runtime_error(stream.str());
|
164
|
-
}
|
165
|
-
if (mIsUsed && mIsRequired && mValues.size() == 0) {
|
166
|
-
std::stringstream stream;
|
167
|
-
stream << mUsedName << ": no value provided.";
|
168
|
-
throw std::runtime_error(stream.str());
|
169
|
-
}
|
170
|
-
}
|
171
|
-
} else {
|
172
|
-
if (mValues.size() != mNumArgs && !mDefaultValue.has_value()) {
|
173
|
-
std::stringstream stream;
|
174
|
-
stream << mUsedName << ": expected " << mNumArgs
|
175
|
-
<< " argument(s). " << mValues.size() << " provided.";
|
176
|
-
throw std::runtime_error(stream.str());
|
177
|
-
}
|
178
|
-
}
|
179
|
-
}
|
180
|
-
|
181
|
-
size_t get_arguments_length() const {
|
182
|
-
return std::accumulate(std::begin(mNames), std::end(mNames), size_t(0),
|
183
|
-
[](const auto &sum, const auto &s) {
|
184
|
-
return sum + s.size() +
|
185
|
-
1; // +1 for space between names
|
186
|
-
});
|
187
|
-
}
|
188
|
-
|
189
|
-
friend std::ostream &operator<<(std::ostream &stream,
|
190
|
-
const Argument &argument) {
|
191
|
-
std::stringstream nameStream;
|
192
|
-
std::copy(std::begin(argument.mNames), std::end(argument.mNames),
|
193
|
-
std::ostream_iterator<std::string>(nameStream, " "));
|
194
|
-
stream << nameStream.str() << "\t" << argument.mHelp;
|
195
|
-
if (argument.mIsRequired)
|
196
|
-
stream << "[Required]";
|
197
|
-
stream << "\n";
|
198
|
-
return stream;
|
199
|
-
}
|
200
|
-
|
201
|
-
template <typename T> bool operator!=(const T &aRhs) const {
|
202
|
-
return !(*this == aRhs);
|
203
|
-
}
|
204
|
-
|
205
|
-
/*
|
206
|
-
* Entry point for template non-container types
|
207
|
-
* @throws std::logic_error in case of incompatible types
|
208
|
-
*/
|
209
|
-
template <typename T>
|
210
|
-
std::enable_if_t<!details::is_container_v<T>, bool> operator==(const T &aRhs) const {
|
211
|
-
return get<T>() == aRhs;
|
212
|
-
}
|
213
|
-
|
214
|
-
/*
|
215
|
-
* Template specialization for containers
|
216
|
-
* @throws std::logic_error in case of incompatible types
|
217
|
-
*/
|
218
|
-
template <typename T>
|
219
|
-
std::enable_if_t<details::is_container_v<T>, bool> operator==(const T &aRhs) const {
|
220
|
-
using ValueType = typename T::value_type;
|
221
|
-
auto tLhs = get<T>();
|
222
|
-
if (tLhs.size() != aRhs.size())
|
223
|
-
return false;
|
224
|
-
else {
|
225
|
-
return std::equal(std::begin(tLhs), std::end(tLhs), std::begin(aRhs),
|
226
|
-
[](const auto &lhs, const auto &rhs) {
|
227
|
-
return std::any_cast<const ValueType &>(lhs) == rhs;
|
228
|
-
});
|
229
|
-
}
|
230
|
-
}
|
231
|
-
|
232
|
-
private:
|
233
|
-
static bool is_integer(const std::string &aValue) {
|
234
|
-
if (aValue.empty() ||
|
235
|
-
((!isdigit(aValue[0])) && (aValue[0] != '-') && (aValue[0] != '+')))
|
236
|
-
return false;
|
237
|
-
char *tPtr;
|
238
|
-
strtol(aValue.c_str(), &tPtr, 10);
|
239
|
-
return (*tPtr == 0);
|
240
|
-
}
|
241
|
-
|
242
|
-
static bool is_float(const std::string &aValue) {
|
243
|
-
std::istringstream tStream(aValue);
|
244
|
-
float tFloat;
|
245
|
-
// noskipws considers leading whitespace invalid
|
246
|
-
tStream >> std::noskipws >> tFloat;
|
247
|
-
// Check the entire string was consumed
|
248
|
-
// and if either failbit or badbit is set
|
249
|
-
return tStream.eof() && !tStream.fail();
|
250
|
-
}
|
251
|
-
|
252
|
-
// If an argument starts with "-" or "--", then it's optional
|
253
|
-
static bool is_optional(const std::string &aName) {
|
254
|
-
return (!aName.empty() && aName[0] == '-' && !is_integer(aName) &&
|
255
|
-
!is_float(aName));
|
256
|
-
}
|
257
|
-
|
258
|
-
static bool is_positional(const std::string &aName) {
|
259
|
-
return !is_optional(aName);
|
260
|
-
}
|
261
|
-
|
262
|
-
/*
|
263
|
-
* Getter for template non-container types
|
264
|
-
* @throws std::logic_error in case of incompatible types
|
265
|
-
*/
|
266
|
-
template <typename T> details::enable_if_not_container<T> get() const {
|
267
|
-
if (!mValues.empty()) {
|
268
|
-
return std::any_cast<T>(mValues.front());
|
269
|
-
}
|
270
|
-
if (mDefaultValue.has_value()) {
|
271
|
-
return std::any_cast<T>(mDefaultValue);
|
272
|
-
}
|
273
|
-
throw std::logic_error("No value provided");
|
274
|
-
}
|
275
|
-
|
276
|
-
/*
|
277
|
-
* Getter for container types
|
278
|
-
* @throws std::logic_error in case of incompatible types
|
279
|
-
*/
|
280
|
-
template <typename CONTAINER> details::enable_if_container<CONTAINER> get() const {
|
281
|
-
using ValueType = typename CONTAINER::value_type;
|
282
|
-
CONTAINER tResult;
|
283
|
-
if (!mValues.empty()) {
|
284
|
-
std::transform(
|
285
|
-
std::begin(mValues), std::end(mValues), std::back_inserter(tResult),
|
286
|
-
[](const auto &value) { return std::any_cast<ValueType>(value); });
|
287
|
-
return tResult;
|
288
|
-
}
|
289
|
-
if (mDefaultValue.has_value()) {
|
290
|
-
const auto &tDefaultValues =
|
291
|
-
std::any_cast<const CONTAINER &>(mDefaultValue);
|
292
|
-
std::transform(std::begin(tDefaultValues), std::end(tDefaultValues),
|
293
|
-
std::back_inserter(tResult), [](const auto &value) {
|
294
|
-
return std::any_cast<ValueType>(value);
|
295
|
-
});
|
296
|
-
return tResult;
|
297
|
-
}
|
298
|
-
throw std::logic_error("No value provided");
|
299
|
-
}
|
300
|
-
|
301
|
-
std::vector<std::string> mNames;
|
302
|
-
std::string mUsedName;
|
303
|
-
std::string mHelp;
|
304
|
-
std::any mDefaultValue;
|
305
|
-
std::any mImplicitValue;
|
306
|
-
std::function<std::any(const std::string &)> mAction =
|
307
|
-
[](const std::string &aValue) { return aValue; };
|
308
|
-
std::vector<std::any> mValues;
|
309
|
-
std::vector<std::string> mRawValues;
|
310
|
-
size_t mNumArgs = 1;
|
311
|
-
bool mIsOptional = false;
|
312
|
-
bool mIsRequired = false;
|
313
|
-
bool mIsUsed = false; // relevant for optional arguments. True if used by user
|
314
|
-
|
315
|
-
public:
|
316
|
-
static constexpr auto mHelpOption = "-h";
|
317
|
-
static constexpr auto mHelpOptionLong = "--help";
|
318
|
-
};
|
319
|
-
|
320
|
-
class ArgumentParser {
|
321
|
-
public:
|
322
|
-
explicit ArgumentParser(std::string aProgramName = {})
|
323
|
-
: mProgramName(std::move(aProgramName)) {
|
324
|
-
add_argument(Argument::mHelpOption, Argument::mHelpOptionLong)
|
325
|
-
.help("show this help message and exit")
|
326
|
-
.nargs(0)
|
327
|
-
.default_value(false)
|
328
|
-
.implicit_value(true);
|
329
|
-
}
|
330
|
-
|
331
|
-
// Parameter packing
|
332
|
-
// Call add_argument with variadic number of string arguments
|
333
|
-
template <typename... Targs> Argument &add_argument(Targs... Fargs) {
|
334
|
-
std::shared_ptr<Argument> tArgument =
|
335
|
-
std::make_shared<Argument>(std::move(Fargs)...);
|
336
|
-
|
337
|
-
if (tArgument->mIsOptional)
|
338
|
-
mOptionalArguments.emplace_back(tArgument);
|
339
|
-
else
|
340
|
-
mPositionalArguments.emplace_back(tArgument);
|
341
|
-
|
342
|
-
for (const auto &mName : tArgument->mNames) {
|
343
|
-
mArgumentMap.insert_or_assign(mName, tArgument);
|
344
|
-
}
|
345
|
-
return *tArgument;
|
346
|
-
}
|
347
|
-
|
348
|
-
// Parameter packed add_parents method
|
349
|
-
// Accepts a variadic number of ArgumentParser objects
|
350
|
-
template <typename... Targs> void add_parents(Targs... Fargs) {
|
351
|
-
const auto tNewParentParsers = {Fargs...};
|
352
|
-
for (const auto &tParentParser : tNewParentParsers) {
|
353
|
-
const auto &tPositionalArguments = tParentParser.mPositionalArguments;
|
354
|
-
std::copy(std::begin(tPositionalArguments),
|
355
|
-
std::end(tPositionalArguments),
|
356
|
-
std::back_inserter(mPositionalArguments));
|
357
|
-
|
358
|
-
const auto &tOptionalArguments = tParentParser.mOptionalArguments;
|
359
|
-
std::copy(std::begin(tOptionalArguments), std::end(tOptionalArguments),
|
360
|
-
std::back_inserter(mOptionalArguments));
|
361
|
-
|
362
|
-
const auto &tArgumentMap = tParentParser.mArgumentMap;
|
363
|
-
for (const auto &[tKey, tValue] : tArgumentMap) {
|
364
|
-
mArgumentMap.insert_or_assign(tKey, tValue);
|
365
|
-
}
|
366
|
-
}
|
367
|
-
std::move(std::begin(tNewParentParsers), std::end(tNewParentParsers),
|
368
|
-
std::back_inserter(mParentParsers));
|
369
|
-
}
|
370
|
-
|
371
|
-
/* Call parse_args_internal - which does all the work
|
372
|
-
* Then, validate the parsed arguments
|
373
|
-
* This variant is used mainly for testing
|
374
|
-
* @throws std::runtime_error in case of any invalid argument
|
375
|
-
*/
|
376
|
-
void parse_args(const std::vector<std::string> &aArguments) {
|
377
|
-
parse_args_internal(aArguments);
|
378
|
-
parse_args_validate();
|
379
|
-
}
|
380
|
-
|
381
|
-
/* Main entry point for parsing command-line arguments using this
|
382
|
-
* ArgumentParser
|
383
|
-
* @throws std::runtime_error in case of any invalid argument
|
384
|
-
*/
|
385
|
-
void parse_args(int argc, const char *const argv[]) {
|
386
|
-
std::vector<std::string> arguments;
|
387
|
-
std::copy(argv, argv + argc, std::back_inserter(arguments));
|
388
|
-
parse_args(arguments);
|
389
|
-
}
|
390
|
-
|
391
|
-
/* Getter enabled for all template types other than std::vector and std::list
|
392
|
-
* @throws std::logic_error in case of an invalid argument name
|
393
|
-
* @throws std::logic_error in case of incompatible types
|
394
|
-
*/
|
395
|
-
template <typename T = std::string> T get(const std::string &aArgumentName) {
|
396
|
-
auto tIterator = mArgumentMap.find(aArgumentName);
|
397
|
-
if (tIterator != mArgumentMap.end()) {
|
398
|
-
return tIterator->second->get<T>();
|
399
|
-
}
|
400
|
-
throw std::logic_error("No such argument");
|
401
|
-
}
|
402
|
-
|
403
|
-
/* Indexing operator. Return a reference to an Argument object
|
404
|
-
* Used in conjuction with Argument.operator== e.g., parser["foo"] == true
|
405
|
-
* @throws std::logic_error in case of an invalid argument name
|
406
|
-
*/
|
407
|
-
Argument &operator[](const std::string &aArgumentName) {
|
408
|
-
auto tIterator = mArgumentMap.find(aArgumentName);
|
409
|
-
if (tIterator != mArgumentMap.end()) {
|
410
|
-
return *(tIterator->second);
|
411
|
-
}
|
412
|
-
throw std::logic_error("No such argument");
|
413
|
-
}
|
414
|
-
|
415
|
-
// Printing the one and only help message
|
416
|
-
// I've stuck with a simple message format, nothing fancy.
|
417
|
-
// TODO: support user-defined help and usage messages for the ArgumentParser
|
418
|
-
std::string print_help() {
|
419
|
-
std::stringstream stream;
|
420
|
-
stream << std::left;
|
421
|
-
stream << "Usage: ./" << mProgramName << " [options] ";
|
422
|
-
size_t tLongestArgumentLength = get_length_of_longest_argument();
|
423
|
-
|
424
|
-
for (const auto &argument : mPositionalArguments) {
|
425
|
-
stream << argument->mNames.front() << " ";
|
426
|
-
}
|
427
|
-
stream << "\n\n";
|
428
|
-
|
429
|
-
if (!mPositionalArguments.empty())
|
430
|
-
stream << "Positional arguments:\n";
|
431
|
-
|
432
|
-
for (const auto &mPositionalArgument : mPositionalArguments) {
|
433
|
-
stream.width(tLongestArgumentLength);
|
434
|
-
stream << *mPositionalArgument;
|
435
|
-
}
|
436
|
-
|
437
|
-
if (!mOptionalArguments.empty())
|
438
|
-
stream << (mPositionalArguments.empty() ? "" : "\n")
|
439
|
-
<< "Options:\n";
|
440
|
-
|
441
|
-
for (const auto &mOptionalArgument : mOptionalArguments) {
|
442
|
-
stream.width(tLongestArgumentLength);
|
443
|
-
stream << *mOptionalArgument;
|
444
|
-
}
|
445
|
-
|
446
|
-
std::cout << stream.str();
|
447
|
-
return stream.str();
|
448
|
-
}
|
449
|
-
|
450
|
-
private:
|
451
|
-
/*
|
452
|
-
* @throws std::runtime_error in case of any invalid argument
|
453
|
-
*/
|
454
|
-
void parse_args_internal(const std::vector<std::string> &aArguments) {
|
455
|
-
if (mProgramName.empty() && !aArguments.empty()) {
|
456
|
-
mProgramName = aArguments.front();
|
457
|
-
}
|
458
|
-
auto end = std::end(aArguments);
|
459
|
-
auto positionalArgumentIt = std::begin(mPositionalArguments);
|
460
|
-
for (auto it = std::next(std::begin(aArguments)); it != end;) {
|
461
|
-
const auto &tCurrentArgument = *it;
|
462
|
-
if (tCurrentArgument == Argument::mHelpOption ||
|
463
|
-
tCurrentArgument == Argument::mHelpOptionLong) {
|
464
|
-
throw std::runtime_error("help called");
|
465
|
-
}
|
466
|
-
if (Argument::is_positional(tCurrentArgument)) {
|
467
|
-
if (positionalArgumentIt == std::end(mPositionalArguments)) {
|
468
|
-
throw std::runtime_error(
|
469
|
-
"Maximum number of positional arguments exceeded");
|
470
|
-
}
|
471
|
-
auto tArgument = *(positionalArgumentIt++);
|
472
|
-
it = tArgument->consume(it, end);
|
473
|
-
} else if (auto tIterator = mArgumentMap.find(tCurrentArgument);
|
474
|
-
tIterator != mArgumentMap.end()) {
|
475
|
-
auto tArgument = tIterator->second;
|
476
|
-
it = tArgument->consume(std::next(it), end, tCurrentArgument);
|
477
|
-
} else if (const auto &tCompoundArgument = tCurrentArgument;
|
478
|
-
tCompoundArgument.size() > 1 && tCompoundArgument[0] == '-' &&
|
479
|
-
tCompoundArgument[1] != '-') {
|
480
|
-
++it;
|
481
|
-
for (size_t j = 1; j < tCompoundArgument.size(); j++) {
|
482
|
-
auto tCurrentArgument = std::string{'-', tCompoundArgument[j]};
|
483
|
-
if (auto tIterator = mArgumentMap.find(tCurrentArgument);
|
484
|
-
tIterator != mArgumentMap.end()) {
|
485
|
-
auto tArgument = tIterator->second;
|
486
|
-
it = tArgument->consume(it, end, tCurrentArgument);
|
487
|
-
} else {
|
488
|
-
throw std::runtime_error("Unknown argument");
|
489
|
-
}
|
490
|
-
}
|
491
|
-
} else {
|
492
|
-
throw std::runtime_error("Unknown argument");
|
493
|
-
}
|
494
|
-
}
|
495
|
-
}
|
496
|
-
|
497
|
-
/*
|
498
|
-
* @throws std::runtime_error in case of any invalid argument
|
499
|
-
*/
|
500
|
-
void parse_args_validate() {
|
501
|
-
// Check if all arguments are parsed
|
502
|
-
std::for_each(std::begin(mArgumentMap), std::end(mArgumentMap),
|
503
|
-
[](const auto &argPair) {
|
504
|
-
const auto &tArgument = argPair.second;
|
505
|
-
tArgument->validate();
|
506
|
-
});
|
507
|
-
}
|
508
|
-
|
509
|
-
// Used by print_help.
|
510
|
-
size_t get_length_of_longest_argument() {
|
511
|
-
if (mArgumentMap.empty())
|
512
|
-
return 0;
|
513
|
-
std::vector<size_t> argumentLengths(mArgumentMap.size());
|
514
|
-
std::transform(std::begin(mArgumentMap), std::end(mArgumentMap),
|
515
|
-
std::begin(argumentLengths), [](const auto &argPair) {
|
516
|
-
const auto &tArgument = argPair.second;
|
517
|
-
return tArgument->get_arguments_length();
|
518
|
-
});
|
519
|
-
return *std::max_element(std::begin(argumentLengths),
|
520
|
-
std::end(argumentLengths));
|
521
|
-
}
|
522
|
-
|
523
|
-
std::string mProgramName;
|
524
|
-
std::vector<ArgumentParser> mParentParsers;
|
525
|
-
std::vector<std::shared_ptr<Argument>> mPositionalArguments;
|
526
|
-
std::vector<std::shared_ptr<Argument>> mOptionalArguments;
|
527
|
-
std::map<std::string, std::shared_ptr<Argument>> mArgumentMap;
|
528
|
-
};
|
529
|
-
|
530
|
-
#define PARSE_ARGS(parser, argc, argv) \
|
531
|
-
try { \
|
532
|
-
parser.parse_args(argc, argv); \
|
533
|
-
} catch (const std::runtime_error &err) { \
|
534
|
-
std::cout << err.what() << std::endl; \
|
535
|
-
parser.print_help(); \
|
536
|
-
exit(0); \
|
537
|
-
}
|
538
|
-
|
539
|
-
} // namespace argparse
|