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