midas-edge 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|