Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2017 Google Inc. |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license that can be |
| 5 | * found in the LICENSE file. |
| 6 | */ |
| 7 | |
| 8 | // ok is an experimental test harness, maybe to replace DM. Key features: |
| 9 | // * work is balanced across separate processes for stability and isolation; |
| 10 | // * ok is entirely opt-in. No more maintaining huge --blacklists. |
| 11 | |
| 12 | #include "SkGraphics.h" |
Florin Malita | ab244f0 | 2017-05-03 19:16:58 +0000 | [diff] [blame] | 13 | #include "SkImage.h" |
Mike Klein | 7ac0483 | 2017-03-25 11:29:41 -0400 | [diff] [blame] | 14 | #include "ok.h" |
Mike Klein | 9827256c | 2017-03-23 15:29:26 -0400 | [diff] [blame] | 15 | #include <chrono> |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 16 | #include <future> |
Mike Klein | 9827256c | 2017-03-23 15:29:26 -0400 | [diff] [blame] | 17 | #include <list> |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 18 | #include <stdio.h> |
| 19 | #include <stdlib.h> |
| 20 | #include <thread> |
Mike Klein | e15a7b5 | 2017-03-29 12:41:13 -0400 | [diff] [blame] | 21 | #include <vector> |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 22 | |
Mike Klein | 61e9902 | 2017-03-23 18:36:39 -0400 | [diff] [blame] | 23 | #if !defined(__has_include) |
| 24 | #define __has_include(x) 0 |
| 25 | #endif |
| 26 | |
Mike Klein | e15a7b5 | 2017-03-29 12:41:13 -0400 | [diff] [blame] | 27 | static thread_local const char* tls_currently_running = ""; |
Mike Klein | 61e9902 | 2017-03-23 18:36:39 -0400 | [diff] [blame] | 28 | |
| 29 | #if __has_include(<execinfo.h>) && __has_include(<fcntl.h>) && __has_include(<signal.h>) |
| 30 | #include <execinfo.h> |
| 31 | #include <fcntl.h> |
| 32 | #include <signal.h> |
| 33 | |
Mike Klein | 200f6da | 2017-03-28 09:30:11 -0400 | [diff] [blame] | 34 | static int log_fd = 2/*stderr*/; |
| 35 | |
| 36 | static void log(const char* msg) { |
| 37 | write(log_fd, msg, strlen(msg)); |
| 38 | } |
Mike Klein | 61e9902 | 2017-03-23 18:36:39 -0400 | [diff] [blame] | 39 | |
| 40 | static void setup_crash_handler() { |
| 41 | static void (*original_handlers[32])(int); |
Mike Klein | 61e9902 | 2017-03-23 18:36:39 -0400 | [diff] [blame] | 42 | for (int sig : std::vector<int>{ SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV }) { |
| 43 | original_handlers[sig] = signal(sig, [](int sig) { |
Mike Klein | 200f6da | 2017-03-28 09:30:11 -0400 | [diff] [blame] | 44 | lockf(log_fd, F_LOCK, 0); |
| 45 | log("\ncaught signal "); |
| 46 | switch (sig) { |
| 47 | #define CASE(s) case s: log(#s); break |
| 48 | CASE(SIGABRT); |
| 49 | CASE(SIGBUS); |
| 50 | CASE(SIGFPE); |
| 51 | CASE(SIGILL); |
| 52 | CASE(SIGSEGV); |
| 53 | #undef CASE |
| 54 | } |
| 55 | log(" while running '"); |
Mike Klein | e15a7b5 | 2017-03-29 12:41:13 -0400 | [diff] [blame] | 56 | log(tls_currently_running); |
Mike Klein | 200f6da | 2017-03-28 09:30:11 -0400 | [diff] [blame] | 57 | log("'\n"); |
Mike Klein | 59eed0d | 2017-03-26 23:22:44 -0400 | [diff] [blame] | 58 | |
Mike Klein | 200f6da | 2017-03-28 09:30:11 -0400 | [diff] [blame] | 59 | void* stack[128]; |
| 60 | int frames = backtrace(stack, sizeof(stack)/sizeof(*stack)); |
| 61 | backtrace_symbols_fd(stack, frames, log_fd); |
| 62 | lockf(log_fd, F_ULOCK, 0); |
Mike Klein | 61e9902 | 2017-03-23 18:36:39 -0400 | [diff] [blame] | 63 | |
Mike Klein | 61e9902 | 2017-03-23 18:36:39 -0400 | [diff] [blame] | 64 | signal(sig, original_handlers[sig]); |
| 65 | raise(sig); |
| 66 | }); |
| 67 | } |
| 68 | } |
| 69 | |
Mike Klein | 200f6da | 2017-03-28 09:30:11 -0400 | [diff] [blame] | 70 | static void defer_logging() { |
| 71 | log_fd = fileno(tmpfile()); |
Mike Klein | 61e9902 | 2017-03-23 18:36:39 -0400 | [diff] [blame] | 72 | atexit([] { |
Mike Klein | 200f6da | 2017-03-28 09:30:11 -0400 | [diff] [blame] | 73 | lseek(log_fd, 0, SEEK_SET); |
Mike Klein | 61e9902 | 2017-03-23 18:36:39 -0400 | [diff] [blame] | 74 | char buf[1024]; |
Mike Klein | 200f6da | 2017-03-28 09:30:11 -0400 | [diff] [blame] | 75 | while (size_t bytes = read(log_fd, buf, sizeof(buf))) { |
Mike Klein | 61e9902 | 2017-03-23 18:36:39 -0400 | [diff] [blame] | 76 | write(2, buf, bytes); |
| 77 | } |
| 78 | }); |
| 79 | } |
Mike Klein | 200f6da | 2017-03-28 09:30:11 -0400 | [diff] [blame] | 80 | |
| 81 | void ok_log(const char* msg) { |
| 82 | lockf(log_fd, F_LOCK, 0); |
| 83 | log("["); |
Mike Klein | e15a7b5 | 2017-03-29 12:41:13 -0400 | [diff] [blame] | 84 | log(tls_currently_running); |
Mike Klein | 200f6da | 2017-03-28 09:30:11 -0400 | [diff] [blame] | 85 | log("]\t"); |
| 86 | log(msg); |
| 87 | log("\n"); |
| 88 | lockf(log_fd, F_ULOCK, 0); |
| 89 | } |
| 90 | |
Mike Klein | 61e9902 | 2017-03-23 18:36:39 -0400 | [diff] [blame] | 91 | #else |
| 92 | static void setup_crash_handler() {} |
Mike Klein | 200f6da | 2017-03-28 09:30:11 -0400 | [diff] [blame] | 93 | static void defer_logging() {} |
| 94 | |
| 95 | void ok_log(const char* msg) { |
| 96 | fprintf(stderr, "%s\n", msg); |
| 97 | } |
Mike Klein | 61e9902 | 2017-03-23 18:36:39 -0400 | [diff] [blame] | 98 | #endif |
| 99 | |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 100 | struct Engine { |
| 101 | virtual ~Engine() {} |
| 102 | virtual bool spawn(std::function<Status(void)>) = 0; |
| 103 | virtual Status wait_one() = 0; |
| 104 | }; |
| 105 | |
| 106 | struct SerialEngine : Engine { |
| 107 | Status last = Status::None; |
| 108 | |
| 109 | bool spawn(std::function<Status(void)> fn) override { |
| 110 | last = fn(); |
| 111 | return true; |
| 112 | } |
| 113 | |
| 114 | Status wait_one() override { |
| 115 | Status s = last; |
| 116 | last = Status::None; |
| 117 | return s; |
| 118 | } |
| 119 | }; |
| 120 | |
| 121 | struct ThreadEngine : Engine { |
Mike Klein | 9827256c | 2017-03-23 15:29:26 -0400 | [diff] [blame] | 122 | std::list<std::future<Status>> live; |
Mike Klein | 9fa9961 | 2017-04-27 09:50:34 -0400 | [diff] [blame] | 123 | const std::chrono::steady_clock::time_point the_past = std::chrono::steady_clock::now(); |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 124 | |
| 125 | bool spawn(std::function<Status(void)> fn) override { |
| 126 | live.push_back(std::async(std::launch::async, fn)); |
| 127 | return true; |
| 128 | } |
| 129 | |
| 130 | Status wait_one() override { |
| 131 | if (live.empty()) { |
| 132 | return Status::None; |
| 133 | } |
Mike Klein | 9827256c | 2017-03-23 15:29:26 -0400 | [diff] [blame] | 134 | |
| 135 | for (;;) { |
| 136 | for (auto it = live.begin(); it != live.end(); it++) { |
Mike Klein | 9fa9961 | 2017-04-27 09:50:34 -0400 | [diff] [blame] | 137 | if (it->wait_until(the_past) == std::future_status::ready) { |
Mike Klein | 9827256c | 2017-03-23 15:29:26 -0400 | [diff] [blame] | 138 | Status s = it->get(); |
| 139 | live.erase(it); |
| 140 | return s; |
| 141 | } |
| 142 | } |
| 143 | } |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 144 | } |
| 145 | }; |
| 146 | |
| 147 | #if defined(_MSC_VER) |
| 148 | using ForkEngine = ThreadEngine; |
| 149 | #else |
| 150 | #include <sys/wait.h> |
| 151 | #include <unistd.h> |
| 152 | |
| 153 | struct ForkEngine : Engine { |
| 154 | bool spawn(std::function<Status(void)> fn) override { |
| 155 | switch (fork()) { |
Mike Klein | 61e9902 | 2017-03-23 18:36:39 -0400 | [diff] [blame] | 156 | case 0: _exit((int)fn()); |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 157 | case -1: return false; |
| 158 | default: return true; |
| 159 | } |
| 160 | } |
| 161 | |
| 162 | Status wait_one() override { |
| 163 | do { |
| 164 | int status; |
| 165 | if (wait(&status) > 0) { |
| 166 | return WIFEXITED(status) ? (Status)WEXITSTATUS(status) |
| 167 | : Status::Crashed; |
| 168 | } |
| 169 | } while (errno == EINTR); |
| 170 | return Status::None; |
| 171 | } |
| 172 | }; |
| 173 | #endif |
| 174 | |
Mike Klein | 7ac0483 | 2017-03-25 11:29:41 -0400 | [diff] [blame] | 175 | struct StreamType { |
Mike Klein | e15a7b5 | 2017-03-29 12:41:13 -0400 | [diff] [blame] | 176 | const char *name, *help; |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 177 | std::unique_ptr<Stream> (*factory)(Options); |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 178 | }; |
Mike Klein | 7ac0483 | 2017-03-25 11:29:41 -0400 | [diff] [blame] | 179 | static std::vector<StreamType> stream_types; |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 180 | |
Mike Klein | 7ac0483 | 2017-03-25 11:29:41 -0400 | [diff] [blame] | 181 | struct DstType { |
Mike Klein | e15a7b5 | 2017-03-29 12:41:13 -0400 | [diff] [blame] | 182 | const char *name, *help; |
Mike Klein | 0222e70 | 2017-03-25 15:53:14 -0400 | [diff] [blame] | 183 | std::unique_ptr<Dst> (*factory)(Options); |
Mike Klein | 51fe971 | 2017-03-24 14:06:47 -0400 | [diff] [blame] | 184 | }; |
Mike Klein | 7ac0483 | 2017-03-25 11:29:41 -0400 | [diff] [blame] | 185 | static std::vector<DstType> dst_types; |
Mike Klein | 51fe971 | 2017-03-24 14:06:47 -0400 | [diff] [blame] | 186 | |
Mike Klein | f5d1a55 | 2017-03-25 12:32:22 -0400 | [diff] [blame] | 187 | struct ViaType { |
Mike Klein | e15a7b5 | 2017-03-29 12:41:13 -0400 | [diff] [blame] | 188 | const char *name, *help; |
Mike Klein | 0222e70 | 2017-03-25 15:53:14 -0400 | [diff] [blame] | 189 | std::unique_ptr<Dst> (*factory)(Options, std::unique_ptr<Dst>); |
Mike Klein | f5d1a55 | 2017-03-25 12:32:22 -0400 | [diff] [blame] | 190 | }; |
| 191 | static std::vector<ViaType> via_types; |
| 192 | |
Mike Klein | e15a7b5 | 2017-03-29 12:41:13 -0400 | [diff] [blame] | 193 | template <typename T> |
| 194 | static std::string help_for(std::vector<T> registered) { |
| 195 | std::string help; |
| 196 | for (auto r : registered) { |
| 197 | help += "\n "; |
| 198 | help += r.name; |
| 199 | help += ": "; |
| 200 | help += r.help; |
| 201 | } |
| 202 | return help; |
| 203 | } |
| 204 | |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 205 | int main(int argc, char** argv) { |
| 206 | SkGraphics::Init(); |
Mike Klein | 61e9902 | 2017-03-23 18:36:39 -0400 | [diff] [blame] | 207 | setup_crash_handler(); |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 208 | |
Mike Klein | e15a7b5 | 2017-03-29 12:41:13 -0400 | [diff] [blame] | 209 | int jobs{1}; |
Mike Klein | 0222e70 | 2017-03-25 15:53:14 -0400 | [diff] [blame] | 210 | std::unique_ptr<Stream> stream; |
Mike Klein | e15a7b5 | 2017-03-29 12:41:13 -0400 | [diff] [blame] | 211 | std::function<std::unique_ptr<Dst>(void)> dst_factory = []{ |
| 212 | // A default Dst that's enough for unit tests and not much else. |
| 213 | struct : Dst { |
| 214 | Status draw(Src* src) override { return src->draw(nullptr); } |
| 215 | sk_sp<SkImage> image() override { return nullptr; } |
| 216 | } dst; |
| 217 | return move_unique(dst); |
| 218 | }; |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 219 | |
| 220 | auto help = [&] { |
Mike Klein | e15a7b5 | 2017-03-29 12:41:13 -0400 | [diff] [blame] | 221 | std::string stream_help = help_for(stream_types), |
| 222 | dst_help = help_for( dst_types), |
| 223 | via_help = help_for( via_types); |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 224 | |
Mike Klein | 5a8da16 | 2017-04-12 11:02:44 -0400 | [diff] [blame] | 225 | printf("%s [-j N] src[:k=v,...] dst[:k=v,...] [via[:k=v,...] ...] \n" |
Mike Klein | f5d1a55 | 2017-03-25 12:32:22 -0400 | [diff] [blame] | 226 | " -j: Run at most N processes at any time. \n" |
| 227 | " If <0, use -N threads instead. \n" |
| 228 | " If 0, use one thread in one process. \n" |
| 229 | " If 1 (default) or -1, auto-detect N. \n" |
Mike Klein | e15a7b5 | 2017-03-29 12:41:13 -0400 | [diff] [blame] | 230 | " src: content to draw%s \n" |
| 231 | " dst: how to draw that content%s \n" |
| 232 | " via: wrappers around dst%s \n" |
| 233 | " Most srcs, dsts and vias have options, e.g. skp:dir=skps sw:ct=565 \n", |
| 234 | argv[0], stream_help.c_str(), dst_help.c_str(), via_help.c_str()); |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 235 | return 1; |
| 236 | }; |
| 237 | |
| 238 | for (int i = 1; i < argc; i++) { |
Mike Klein | 5a8da16 | 2017-04-12 11:02:44 -0400 | [diff] [blame] | 239 | if (0 == strcmp("-j", argv[i])) { jobs = atoi(argv[++i]); } |
| 240 | if (0 == strcmp("-h", argv[i])) { return help(); } |
| 241 | if (0 == strcmp("--help", argv[i])) { return help(); } |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 242 | |
Mike Klein | 7ac0483 | 2017-03-25 11:29:41 -0400 | [diff] [blame] | 243 | for (auto s : stream_types) { |
Mike Klein | 51fe971 | 2017-03-24 14:06:47 -0400 | [diff] [blame] | 244 | size_t len = strlen(s.name); |
| 245 | if (0 == strncmp(s.name, argv[i], len)) { |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 246 | switch (argv[i][len]) { |
| 247 | case ':': len++; |
Mike Klein | 51fe971 | 2017-03-24 14:06:47 -0400 | [diff] [blame] | 248 | case '\0': stream = s.factory(Options{argv[i]+len}); |
| 249 | } |
| 250 | } |
| 251 | } |
Mike Klein | 7ac0483 | 2017-03-25 11:29:41 -0400 | [diff] [blame] | 252 | for (auto d : dst_types) { |
Mike Klein | 51fe971 | 2017-03-24 14:06:47 -0400 | [diff] [blame] | 253 | size_t len = strlen(d.name); |
| 254 | if (0 == strncmp(d.name, argv[i], len)) { |
| 255 | switch (argv[i][len]) { |
| 256 | case ':': len++; |
Mike Klein | 0222e70 | 2017-03-25 15:53:14 -0400 | [diff] [blame] | 257 | case '\0': dst_factory = [=]{ |
| 258 | return d.factory(Options{argv[i]+len}); |
Mike Klein | f5d1a55 | 2017-03-25 12:32:22 -0400 | [diff] [blame] | 259 | }; |
| 260 | } |
| 261 | } |
| 262 | } |
| 263 | for (auto v : via_types) { |
| 264 | size_t len = strlen(v.name); |
| 265 | if (0 == strncmp(v.name, argv[i], len)) { |
Mike Klein | 0222e70 | 2017-03-25 15:53:14 -0400 | [diff] [blame] | 266 | if (!dst_factory) { return help(); } |
Mike Klein | f5d1a55 | 2017-03-25 12:32:22 -0400 | [diff] [blame] | 267 | switch (argv[i][len]) { |
| 268 | case ':': len++; |
Mike Klein | 0222e70 | 2017-03-25 15:53:14 -0400 | [diff] [blame] | 269 | case '\0': dst_factory = [=]{ |
| 270 | return v.factory(Options{argv[i]+len}, dst_factory()); |
Mike Klein | 7ac0483 | 2017-03-25 11:29:41 -0400 | [diff] [blame] | 271 | }; |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 272 | } |
| 273 | } |
| 274 | } |
| 275 | } |
Mike Klein | d63442d | 2017-03-27 14:16:04 -0400 | [diff] [blame] | 276 | if (!stream) { return help(); } |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 277 | |
| 278 | std::unique_ptr<Engine> engine; |
Mike Klein | 200f6da | 2017-03-28 09:30:11 -0400 | [diff] [blame] | 279 | if (jobs == 0) { engine.reset(new SerialEngine); } |
| 280 | if (jobs > 0) { engine.reset(new ForkEngine); defer_logging(); } |
| 281 | if (jobs < 0) { engine.reset(new ThreadEngine); jobs = -jobs; } |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 282 | |
| 283 | if (jobs == 1) { jobs = std::thread::hardware_concurrency(); } |
| 284 | |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 285 | int ok = 0, failed = 0, crashed = 0, skipped = 0; |
| 286 | |
| 287 | auto update_stats = [&](Status s) { |
| 288 | switch (s) { |
| 289 | case Status::OK: ok++; break; |
| 290 | case Status::Failed: failed++; break; |
| 291 | case Status::Crashed: crashed++; break; |
| 292 | case Status::Skipped: skipped++; break; |
| 293 | case Status::None: return; |
| 294 | } |
| 295 | const char* leader = "\r"; |
| 296 | auto print = [&](int count, const char* label) { |
| 297 | if (count) { |
| 298 | printf("%s%d %s", leader, count, label); |
| 299 | leader = ", "; |
| 300 | } |
| 301 | }; |
| 302 | print(ok, "ok"); |
| 303 | print(failed, "failed"); |
| 304 | print(crashed, "crashed"); |
| 305 | print(skipped, "skipped"); |
| 306 | fflush(stdout); |
| 307 | }; |
| 308 | |
| 309 | auto spawn = [&](std::function<Status(void)> fn) { |
| 310 | if (--jobs < 0) { |
| 311 | update_stats(engine->wait_one()); |
| 312 | } |
| 313 | while (!engine->spawn(fn)) { |
| 314 | update_stats(engine->wait_one()); |
| 315 | } |
| 316 | }; |
| 317 | |
| 318 | for (std::unique_ptr<Src> owned = stream->next(); owned; owned = stream->next()) { |
| 319 | Src* raw = owned.release(); // Can't move std::unique_ptr into a lambda in C++11. :( |
| 320 | spawn([=] { |
| 321 | std::unique_ptr<Src> src{raw}; |
| 322 | |
Mike Klein | e15a7b5 | 2017-03-29 12:41:13 -0400 | [diff] [blame] | 323 | std::string name = src->name(); |
| 324 | tls_currently_running = name.c_str(); |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 325 | |
Mike Klein | e15a7b5 | 2017-03-29 12:41:13 -0400 | [diff] [blame] | 326 | return dst_factory()->draw(src.get()); |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 327 | }); |
| 328 | } |
| 329 | |
| 330 | for (Status s = Status::OK; s != Status::None; ) { |
| 331 | s = engine->wait_one(); |
| 332 | update_stats(s); |
| 333 | } |
| 334 | printf("\n"); |
| 335 | return (failed || crashed) ? 1 : 0; |
| 336 | } |
Mike Klein | 7ac0483 | 2017-03-25 11:29:41 -0400 | [diff] [blame] | 337 | |
| 338 | |
Mike Klein | e15a7b5 | 2017-03-29 12:41:13 -0400 | [diff] [blame] | 339 | Register::Register(const char* name, const char* help, |
| 340 | std::unique_ptr<Stream> (*factory)(Options)) { |
| 341 | stream_types.push_back(StreamType{name, help, factory}); |
Mike Klein | 7ac0483 | 2017-03-25 11:29:41 -0400 | [diff] [blame] | 342 | } |
Mike Klein | e15a7b5 | 2017-03-29 12:41:13 -0400 | [diff] [blame] | 343 | Register::Register(const char* name, const char* help, |
| 344 | std::unique_ptr<Dst> (*factory)(Options)) { |
| 345 | dst_types.push_back(DstType{name, help, factory}); |
Mike Klein | 7ac0483 | 2017-03-25 11:29:41 -0400 | [diff] [blame] | 346 | } |
Mike Klein | e15a7b5 | 2017-03-29 12:41:13 -0400 | [diff] [blame] | 347 | Register::Register(const char* name, const char* help, |
Mike Klein | 0222e70 | 2017-03-25 15:53:14 -0400 | [diff] [blame] | 348 | std::unique_ptr<Dst> (*factory)(Options, std::unique_ptr<Dst>)) { |
Mike Klein | e15a7b5 | 2017-03-29 12:41:13 -0400 | [diff] [blame] | 349 | via_types.push_back(ViaType{name, help, factory}); |
Mike Klein | f5d1a55 | 2017-03-25 12:32:22 -0400 | [diff] [blame] | 350 | } |
Mike Klein | 7ac0483 | 2017-03-25 11:29:41 -0400 | [diff] [blame] | 351 | |
| 352 | Options::Options(std::string str) { |
| 353 | std::string k,v, *curr = &k; |
| 354 | for (auto c : str) { |
| 355 | switch(c) { |
Mike Klein | 88f9c1e | 2017-03-27 12:43:44 -0400 | [diff] [blame] | 356 | case ',': (*this)[k] = v; |
Mike Klein | 7ac0483 | 2017-03-25 11:29:41 -0400 | [diff] [blame] | 357 | curr = &(k = ""); |
| 358 | break; |
| 359 | case '=': curr = &(v = ""); |
| 360 | break; |
| 361 | default: *curr += c; |
| 362 | } |
| 363 | } |
Mike Klein | 88f9c1e | 2017-03-27 12:43:44 -0400 | [diff] [blame] | 364 | (*this)[k] = v; |
Mike Klein | 7ac0483 | 2017-03-25 11:29:41 -0400 | [diff] [blame] | 365 | } |
| 366 | |
Mike Klein | 88f9c1e | 2017-03-27 12:43:44 -0400 | [diff] [blame] | 367 | std::string& Options::operator[](std::string k) { return this->kv[k]; } |
| 368 | |
Mike Klein | 7ac0483 | 2017-03-25 11:29:41 -0400 | [diff] [blame] | 369 | std::string Options::operator()(std::string k, std::string fallback) const { |
| 370 | for (auto it = kv.find(k); it != kv.end(); ) { |
| 371 | return it->second; |
| 372 | } |
| 373 | return fallback; |
| 374 | } |