-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathargparse.hpp
162 lines (129 loc) · 4.39 KB
/
argparse.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#ifndef SLAP_ARGPARSE_HPP
#define SLAP_ARGPARSE_HPP
#include "slice.hpp"
#include <sstream>
#include <iomanip>
namespace argparse {
using namespace slice;
// using the slice monad on list<const char*> with item values
using thrower = std::function<void()>;
using item = std::pair<const char*, thrower>;
template<class T>
static const auto argument(const char* name) {
return pop() >> [=](const char* value) {
T result;
using std::operator>>;
if(std::stringstream(value) >> result) {
// el cheapo type erasure: close over result and throw its captured
// address to recover it (must be cought with the right type)
const item res = {name, [result] { throw &result; }};
return pure(res);
}
throw std::runtime_error("type error when parsing argument "
+ tool::quote(name));
};
};
template<class T>
static const auto option(const char* name) {
const std::string target = "--" + std::string(name);
return pop_if([=](const char* item) {
return item == target;
}) >> argument<T>;
};
static const auto flag(const char* name) {
const auto parser = [name](std::string target, bool value) {
return pop_if([=](const char* item) {
return item == target;
}) >> [name, value](const char*) {
const item res = {name, [value] { throw &value; }};
return pure(res);
};
};
return parser("--" + std::string(name), true)
| parser("--no-" + std::string(name), false);
};
////////////////////////////////////////////////////////////////////////////////
class result {
std::map<std::string, thrower> parsed;
public:
inline void set(const char* name, thrower value) {
if(!parsed.emplace(name, value).second) {
throw std::runtime_error("duplicate option " + tool::quote(name));
}
}
template<class T>
const T* get(const char* name) const {
auto it = parsed.find(name);
if(it == parsed.end()) return nullptr;
try {
it->second();
} catch(const T* result) {
return result;
} catch(...) {
throw std::runtime_error("type error when accessing argument "
+ tool::quote(name));
}
throw std::logic_error("thrower did not throw");
}
inline bool flag(const char* name, bool default_value) const {
if(auto ptr = get<bool>(name)) {
return *ptr;
} else {
return default_value;
}
}
};
class parser {
monad<const char*, item> options = fail<item>();
std::vector<std::pair<std::string, maybe<std::string>>> help;
void add(std::string name, const char* doc) {
help.emplace_back(name, doc ? just(std::string(doc)) : maybe<std::string>());
}
public:
result parse(int argc, const char** argv) const {
const list<const char*> args = make_list(argv, argv + argc);
auto first = pop()(args);
assert(first);
const std::string name = first.get();
result res;
slice<const char*, item> current = {{}, first.rest};
while((current = options(current.rest))) {
res.set(current.get().first, current.get().second);
}
if(current.rest) {
std::stringstream ss;
ss << "unprocessed options: " << current.rest;
throw std::runtime_error(ss.str());
}
return res;
}
parser& flag(const char* name, const char* doc=nullptr) {
add("--" + std::string(name), doc);
options = argparse::flag(name) | options;
return *this;
}
template<class T>
parser& option(const char* name, const char* doc=nullptr) {
add("--" + std::string(name), doc);
options = argparse::option<T>(name) | options;
return *this;
}
template<class T>
parser& argument(const char* name, const char* doc=nullptr) {
add(name, doc);
options = options | argparse::argument<T>(name);
return *this;
}
void describe(std::ostream& out) const {
out << "available options:" << std::endl;
std::size_t width = 24;
for(auto it : help) {
out << "\t" << std::setw(width) << std::left << it.first;
if(it.second) out << std::setw(width) << std::left << it.second.get();
out << std::endl;
}
}
};
// TODO higher-level class that provides help message etc
}
#endif