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 | 9827256 | 2017-03-23 15:29:26 -0400 | [diff] [blame] | 15 | #include <chrono> |
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> |
Mike Klein | e15a7b5 | 2017-03-29 12:41:13 -0400 | [diff] [blame] | 19 | #include <vector> |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 20 | |
Mike Klein | 61e9902 | 2017-03-23 18:36:39 -0400 | [diff] [blame] | 21 | #if !defined(__has_include) |
| 22 | #define __has_include(x) 0 |
| 23 | #endif |
| 24 | |
Mike Klein | e15a7b5 | 2017-03-29 12:41:13 -0400 | [diff] [blame] | 25 | static thread_local const char* tls_currently_running = ""; |
Mike Klein | 61e9902 | 2017-03-23 18:36:39 -0400 | [diff] [blame] | 26 | |
| 27 | #if __has_include(<execinfo.h>) && __has_include(<fcntl.h>) && __has_include(<signal.h>) |
| 28 | #include <execinfo.h> |
| 29 | #include <fcntl.h> |
| 30 | #include <signal.h> |
| 31 | |
Mike Klein | 200f6da | 2017-03-28 09:30:11 -0400 | [diff] [blame] | 32 | static int log_fd = 2/*stderr*/; |
| 33 | |
| 34 | static void log(const char* msg) { |
| 35 | write(log_fd, msg, strlen(msg)); |
| 36 | } |
Mike Klein | 61e9902 | 2017-03-23 18:36:39 -0400 | [diff] [blame] | 37 | |
| 38 | static void setup_crash_handler() { |
| 39 | static void (*original_handlers[32])(int); |
Mike Klein | 61e9902 | 2017-03-23 18:36:39 -0400 | [diff] [blame] | 40 | for (int sig : std::vector<int>{ SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV }) { |
| 41 | original_handlers[sig] = signal(sig, [](int sig) { |
Mike Klein | 200f6da | 2017-03-28 09:30:11 -0400 | [diff] [blame] | 42 | lockf(log_fd, F_LOCK, 0); |
| 43 | log("\ncaught signal "); |
| 44 | switch (sig) { |
| 45 | #define CASE(s) case s: log(#s); break |
| 46 | CASE(SIGABRT); |
| 47 | CASE(SIGBUS); |
| 48 | CASE(SIGFPE); |
| 49 | CASE(SIGILL); |
| 50 | CASE(SIGSEGV); |
| 51 | #undef CASE |
| 52 | } |
| 53 | log(" while running '"); |
Mike Klein | e15a7b5 | 2017-03-29 12:41:13 -0400 | [diff] [blame] | 54 | log(tls_currently_running); |
Mike Klein | 200f6da | 2017-03-28 09:30:11 -0400 | [diff] [blame] | 55 | log("'\n"); |
Mike Klein | 59eed0d | 2017-03-26 23:22:44 -0400 | [diff] [blame] | 56 | |
Mike Klein | 200f6da | 2017-03-28 09:30:11 -0400 | [diff] [blame] | 57 | void* stack[128]; |
| 58 | int frames = backtrace(stack, sizeof(stack)/sizeof(*stack)); |
| 59 | backtrace_symbols_fd(stack, frames, log_fd); |
| 60 | lockf(log_fd, F_ULOCK, 0); |
Mike Klein | 61e9902 | 2017-03-23 18:36:39 -0400 | [diff] [blame] | 61 | |
Mike Klein | 61e9902 | 2017-03-23 18:36:39 -0400 | [diff] [blame] | 62 | signal(sig, original_handlers[sig]); |
| 63 | raise(sig); |
| 64 | }); |
| 65 | } |
| 66 | } |
| 67 | |
Mike Klein | 200f6da | 2017-03-28 09:30:11 -0400 | [diff] [blame] | 68 | static void defer_logging() { |
| 69 | log_fd = fileno(tmpfile()); |
Mike Klein | 7371491 | 2017-07-26 18:30:57 +0000 | [diff] [blame] | 70 | atexit([] { |
| 71 | lseek(log_fd, 0, SEEK_SET); |
| 72 | char buf[1024]; |
| 73 | while (size_t bytes = read(log_fd, buf, sizeof(buf))) { |
| 74 | write(2, buf, bytes); |
| 75 | } |
| 76 | }); |
Mike Klein | 61e9902 | 2017-03-23 18:36:39 -0400 | [diff] [blame] | 77 | } |
Mike Klein | 200f6da | 2017-03-28 09:30:11 -0400 | [diff] [blame] | 78 | |
| 79 | void ok_log(const char* msg) { |
| 80 | lockf(log_fd, F_LOCK, 0); |
| 81 | log("["); |
Mike Klein | e15a7b5 | 2017-03-29 12:41:13 -0400 | [diff] [blame] | 82 | log(tls_currently_running); |
Mike Klein | 200f6da | 2017-03-28 09:30:11 -0400 | [diff] [blame] | 83 | log("]\t"); |
| 84 | log(msg); |
| 85 | log("\n"); |
| 86 | lockf(log_fd, F_ULOCK, 0); |
| 87 | } |
| 88 | |
Mike Klein | 61e9902 | 2017-03-23 18:36:39 -0400 | [diff] [blame] | 89 | #else |
| 90 | static void setup_crash_handler() {} |
Mike Klein | 200f6da | 2017-03-28 09:30:11 -0400 | [diff] [blame] | 91 | static void defer_logging() {} |
| 92 | |
| 93 | void ok_log(const char* msg) { |
Mike Klein | 84c5435 | 2017-08-29 19:59:57 -0400 | [diff] [blame^] | 94 | fprintf(stderr, "[%s]\t%s\n", tls_currently_running, msg); |
Mike Klein | 200f6da | 2017-03-28 09:30:11 -0400 | [diff] [blame] | 95 | } |
Mike Klein | 61e9902 | 2017-03-23 18:36:39 -0400 | [diff] [blame] | 96 | #endif |
| 97 | |
Mike Klein | 154e6da | 2017-07-26 15:13:47 -0400 | [diff] [blame] | 98 | struct EngineType { |
| 99 | const char *name, *help; |
| 100 | std::unique_ptr<Engine> (*factory)(Options); |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 101 | }; |
Mike Klein | 154e6da | 2017-07-26 15:13:47 -0400 | [diff] [blame] | 102 | static std::vector<EngineType> engine_types; |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 103 | |
Mike Klein | 7ac0483 | 2017-03-25 11:29:41 -0400 | [diff] [blame] | 104 | struct StreamType { |
Mike Klein | e15a7b5 | 2017-03-29 12:41:13 -0400 | [diff] [blame] | 105 | const char *name, *help; |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 106 | std::unique_ptr<Stream> (*factory)(Options); |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 107 | }; |
Mike Klein | 7ac0483 | 2017-03-25 11:29:41 -0400 | [diff] [blame] | 108 | static std::vector<StreamType> stream_types; |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 109 | |
Mike Klein | 7ac0483 | 2017-03-25 11:29:41 -0400 | [diff] [blame] | 110 | struct DstType { |
Mike Klein | e15a7b5 | 2017-03-29 12:41:13 -0400 | [diff] [blame] | 111 | const char *name, *help; |
Mike Klein | 0222e70 | 2017-03-25 15:53:14 -0400 | [diff] [blame] | 112 | std::unique_ptr<Dst> (*factory)(Options); |
Mike Klein | 51fe971 | 2017-03-24 14:06:47 -0400 | [diff] [blame] | 113 | }; |
Mike Klein | 7ac0483 | 2017-03-25 11:29:41 -0400 | [diff] [blame] | 114 | static std::vector<DstType> dst_types; |
Mike Klein | 51fe971 | 2017-03-24 14:06:47 -0400 | [diff] [blame] | 115 | |
Mike Klein | f5d1a55 | 2017-03-25 12:32:22 -0400 | [diff] [blame] | 116 | struct ViaType { |
Mike Klein | e15a7b5 | 2017-03-29 12:41:13 -0400 | [diff] [blame] | 117 | const char *name, *help; |
Mike Klein | 0222e70 | 2017-03-25 15:53:14 -0400 | [diff] [blame] | 118 | std::unique_ptr<Dst> (*factory)(Options, std::unique_ptr<Dst>); |
Mike Klein | f5d1a55 | 2017-03-25 12:32:22 -0400 | [diff] [blame] | 119 | }; |
| 120 | static std::vector<ViaType> via_types; |
| 121 | |
Mike Klein | e15a7b5 | 2017-03-29 12:41:13 -0400 | [diff] [blame] | 122 | template <typename T> |
| 123 | static std::string help_for(std::vector<T> registered) { |
| 124 | std::string help; |
| 125 | for (auto r : registered) { |
| 126 | help += "\n "; |
| 127 | help += r.name; |
| 128 | help += ": "; |
| 129 | help += r.help; |
| 130 | } |
| 131 | return help; |
| 132 | } |
| 133 | |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 134 | int main(int argc, char** argv) { |
| 135 | SkGraphics::Init(); |
Mike Klein | 61e9902 | 2017-03-23 18:36:39 -0400 | [diff] [blame] | 136 | setup_crash_handler(); |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 137 | |
Mike Klein | 154e6da | 2017-07-26 15:13:47 -0400 | [diff] [blame] | 138 | std::unique_ptr<Engine> engine; |
Mike Klein | 0222e70 | 2017-03-25 15:53:14 -0400 | [diff] [blame] | 139 | std::unique_ptr<Stream> stream; |
Mike Klein | e15a7b5 | 2017-03-29 12:41:13 -0400 | [diff] [blame] | 140 | std::function<std::unique_ptr<Dst>(void)> dst_factory = []{ |
| 141 | // A default Dst that's enough for unit tests and not much else. |
| 142 | struct : Dst { |
| 143 | Status draw(Src* src) override { return src->draw(nullptr); } |
| 144 | sk_sp<SkImage> image() override { return nullptr; } |
| 145 | } dst; |
| 146 | return move_unique(dst); |
| 147 | }; |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 148 | |
| 149 | auto help = [&] { |
Mike Klein | 154e6da | 2017-07-26 15:13:47 -0400 | [diff] [blame] | 150 | std::string engine_help = help_for(engine_types), |
| 151 | stream_help = help_for(stream_types), |
Mike Klein | e15a7b5 | 2017-03-29 12:41:13 -0400 | [diff] [blame] | 152 | dst_help = help_for( dst_types), |
| 153 | via_help = help_for( via_types); |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 154 | |
Mike Klein | 154e6da | 2017-07-26 15:13:47 -0400 | [diff] [blame] | 155 | printf("%s [engine] src[:k=v,...] dst[:k=v,...] [via[:k=v,...] ...] \n" |
| 156 | " engine: how to execute tasks%s \n" |
Mike Klein | e15a7b5 | 2017-03-29 12:41:13 -0400 | [diff] [blame] | 157 | " src: content to draw%s \n" |
| 158 | " dst: how to draw that content%s \n" |
| 159 | " via: wrappers around dst%s \n" |
| 160 | " Most srcs, dsts and vias have options, e.g. skp:dir=skps sw:ct=565 \n", |
Mike Klein | 154e6da | 2017-07-26 15:13:47 -0400 | [diff] [blame] | 161 | argv[0], |
| 162 | engine_help.c_str(), stream_help.c_str(), dst_help.c_str(), via_help.c_str()); |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 163 | return 1; |
| 164 | }; |
| 165 | |
| 166 | for (int i = 1; i < argc; i++) { |
Mike Klein | 5a8da16 | 2017-04-12 11:02:44 -0400 | [diff] [blame] | 167 | if (0 == strcmp("-h", argv[i])) { return help(); } |
| 168 | if (0 == strcmp("--help", argv[i])) { return help(); } |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 169 | |
Mike Klein | 154e6da | 2017-07-26 15:13:47 -0400 | [diff] [blame] | 170 | for (auto e : engine_types) { |
| 171 | size_t len = strlen(e.name); |
| 172 | if (0 == strncmp(e.name, argv[i], len)) { |
| 173 | switch (argv[i][len]) { |
| 174 | case ':': len++; |
| 175 | case '\0': engine = e.factory(Options{argv[i]+len}); |
| 176 | } |
| 177 | } |
| 178 | } |
| 179 | |
Mike Klein | 7ac0483 | 2017-03-25 11:29:41 -0400 | [diff] [blame] | 180 | for (auto s : stream_types) { |
Mike Klein | 51fe971 | 2017-03-24 14:06:47 -0400 | [diff] [blame] | 181 | size_t len = strlen(s.name); |
| 182 | if (0 == strncmp(s.name, argv[i], len)) { |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 183 | switch (argv[i][len]) { |
| 184 | case ':': len++; |
Mike Klein | 51fe971 | 2017-03-24 14:06:47 -0400 | [diff] [blame] | 185 | case '\0': stream = s.factory(Options{argv[i]+len}); |
| 186 | } |
| 187 | } |
| 188 | } |
Mike Klein | 7ac0483 | 2017-03-25 11:29:41 -0400 | [diff] [blame] | 189 | for (auto d : dst_types) { |
Mike Klein | 51fe971 | 2017-03-24 14:06:47 -0400 | [diff] [blame] | 190 | size_t len = strlen(d.name); |
| 191 | if (0 == strncmp(d.name, argv[i], len)) { |
| 192 | switch (argv[i][len]) { |
| 193 | case ':': len++; |
Mike Klein | 0222e70 | 2017-03-25 15:53:14 -0400 | [diff] [blame] | 194 | case '\0': dst_factory = [=]{ |
| 195 | return d.factory(Options{argv[i]+len}); |
Mike Klein | f5d1a55 | 2017-03-25 12:32:22 -0400 | [diff] [blame] | 196 | }; |
| 197 | } |
| 198 | } |
| 199 | } |
| 200 | for (auto v : via_types) { |
| 201 | size_t len = strlen(v.name); |
| 202 | if (0 == strncmp(v.name, argv[i], len)) { |
Mike Klein | 0222e70 | 2017-03-25 15:53:14 -0400 | [diff] [blame] | 203 | if (!dst_factory) { return help(); } |
Mike Klein | f5d1a55 | 2017-03-25 12:32:22 -0400 | [diff] [blame] | 204 | switch (argv[i][len]) { |
| 205 | case ':': len++; |
Mike Klein | 0222e70 | 2017-03-25 15:53:14 -0400 | [diff] [blame] | 206 | case '\0': dst_factory = [=]{ |
| 207 | return v.factory(Options{argv[i]+len}, dst_factory()); |
Mike Klein | 7ac0483 | 2017-03-25 11:29:41 -0400 | [diff] [blame] | 208 | }; |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 209 | } |
| 210 | } |
| 211 | } |
| 212 | } |
Mike Klein | d63442d | 2017-03-27 14:16:04 -0400 | [diff] [blame] | 213 | if (!stream) { return help(); } |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 214 | |
Mike Klein | 154e6da | 2017-07-26 15:13:47 -0400 | [diff] [blame] | 215 | if (!engine) { engine = engine_types.back().factory(Options{}); } |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 216 | |
Mike Klein | 154e6da | 2017-07-26 15:13:47 -0400 | [diff] [blame] | 217 | // If we know engine->spawn() will never crash, we can defer logging until we exit. |
| 218 | if (engine->crashproof()) { |
| 219 | defer_logging(); |
| 220 | } |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 221 | |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 222 | int ok = 0, failed = 0, crashed = 0, skipped = 0; |
| 223 | |
| 224 | auto update_stats = [&](Status s) { |
| 225 | switch (s) { |
| 226 | case Status::OK: ok++; break; |
| 227 | case Status::Failed: failed++; break; |
| 228 | case Status::Crashed: crashed++; break; |
| 229 | case Status::Skipped: skipped++; break; |
| 230 | case Status::None: return; |
| 231 | } |
| 232 | const char* leader = "\r"; |
| 233 | auto print = [&](int count, const char* label) { |
| 234 | if (count) { |
| 235 | printf("%s%d %s", leader, count, label); |
| 236 | leader = ", "; |
| 237 | } |
| 238 | }; |
| 239 | print(ok, "ok"); |
| 240 | print(failed, "failed"); |
| 241 | print(crashed, "crashed"); |
| 242 | print(skipped, "skipped"); |
| 243 | fflush(stdout); |
| 244 | }; |
| 245 | |
Mike Klein | 154e6da | 2017-07-26 15:13:47 -0400 | [diff] [blame] | 246 | std::list<std::future<Status>> live; |
| 247 | const auto the_past = std::chrono::steady_clock::now(); |
| 248 | |
| 249 | auto wait_one = [&] { |
| 250 | if (live.empty()) { |
| 251 | return Status::None; |
| 252 | } |
| 253 | |
| 254 | for (;;) { |
| 255 | for (auto it = live.begin(); it != live.end(); it++) { |
| 256 | if (it->wait_until(the_past) != std::future_status::timeout) { |
| 257 | Status s = it->get(); |
| 258 | live.erase(it); |
| 259 | return s; |
| 260 | } |
| 261 | } |
| 262 | } |
| 263 | }; |
| 264 | |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 265 | auto spawn = [&](std::function<Status(void)> fn) { |
Mike Klein | 154e6da | 2017-07-26 15:13:47 -0400 | [diff] [blame] | 266 | std::future<Status> status; |
| 267 | for (;;) { |
| 268 | status = engine->spawn(fn); |
| 269 | if (status.valid()) { |
| 270 | break; |
| 271 | } |
| 272 | update_stats(wait_one()); |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 273 | } |
Mike Klein | 154e6da | 2017-07-26 15:13:47 -0400 | [diff] [blame] | 274 | live.push_back(std::move(status)); |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 275 | }; |
| 276 | |
| 277 | for (std::unique_ptr<Src> owned = stream->next(); owned; owned = stream->next()) { |
| 278 | Src* raw = owned.release(); // Can't move std::unique_ptr into a lambda in C++11. :( |
| 279 | spawn([=] { |
| 280 | std::unique_ptr<Src> src{raw}; |
| 281 | |
Mike Klein | e15a7b5 | 2017-03-29 12:41:13 -0400 | [diff] [blame] | 282 | std::string name = src->name(); |
| 283 | tls_currently_running = name.c_str(); |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 284 | |
Mike Klein | e15a7b5 | 2017-03-29 12:41:13 -0400 | [diff] [blame] | 285 | return dst_factory()->draw(src.get()); |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 286 | }); |
| 287 | } |
| 288 | |
| 289 | for (Status s = Status::OK; s != Status::None; ) { |
Mike Klein | 154e6da | 2017-07-26 15:13:47 -0400 | [diff] [blame] | 290 | s = wait_one(); |
Mike Klein | 06432b2 | 2017-03-21 13:14:33 -0400 | [diff] [blame] | 291 | update_stats(s); |
| 292 | } |
| 293 | printf("\n"); |
| 294 | return (failed || crashed) ? 1 : 0; |
| 295 | } |
Mike Klein | 7ac0483 | 2017-03-25 11:29:41 -0400 | [diff] [blame] | 296 | |
| 297 | |
Mike Klein | e15a7b5 | 2017-03-29 12:41:13 -0400 | [diff] [blame] | 298 | Register::Register(const char* name, const char* help, |
Mike Klein | 154e6da | 2017-07-26 15:13:47 -0400 | [diff] [blame] | 299 | std::unique_ptr<Engine> (*factory)(Options)) { |
| 300 | engine_types.push_back(EngineType{name, help, factory}); |
| 301 | } |
| 302 | Register::Register(const char* name, const char* help, |
Mike Klein | e15a7b5 | 2017-03-29 12:41:13 -0400 | [diff] [blame] | 303 | std::unique_ptr<Stream> (*factory)(Options)) { |
| 304 | stream_types.push_back(StreamType{name, help, factory}); |
Mike Klein | 7ac0483 | 2017-03-25 11:29:41 -0400 | [diff] [blame] | 305 | } |
Mike Klein | e15a7b5 | 2017-03-29 12:41:13 -0400 | [diff] [blame] | 306 | Register::Register(const char* name, const char* help, |
| 307 | std::unique_ptr<Dst> (*factory)(Options)) { |
| 308 | dst_types.push_back(DstType{name, help, factory}); |
Mike Klein | 7ac0483 | 2017-03-25 11:29:41 -0400 | [diff] [blame] | 309 | } |
Mike Klein | e15a7b5 | 2017-03-29 12:41:13 -0400 | [diff] [blame] | 310 | Register::Register(const char* name, const char* help, |
Mike Klein | 0222e70 | 2017-03-25 15:53:14 -0400 | [diff] [blame] | 311 | std::unique_ptr<Dst> (*factory)(Options, std::unique_ptr<Dst>)) { |
Mike Klein | e15a7b5 | 2017-03-29 12:41:13 -0400 | [diff] [blame] | 312 | via_types.push_back(ViaType{name, help, factory}); |
Mike Klein | f5d1a55 | 2017-03-25 12:32:22 -0400 | [diff] [blame] | 313 | } |
Mike Klein | 7ac0483 | 2017-03-25 11:29:41 -0400 | [diff] [blame] | 314 | |
| 315 | Options::Options(std::string str) { |
| 316 | std::string k,v, *curr = &k; |
| 317 | for (auto c : str) { |
| 318 | switch(c) { |
Mike Klein | 88f9c1e | 2017-03-27 12:43:44 -0400 | [diff] [blame] | 319 | case ',': (*this)[k] = v; |
Mike Klein | 7ac0483 | 2017-03-25 11:29:41 -0400 | [diff] [blame] | 320 | curr = &(k = ""); |
| 321 | break; |
| 322 | case '=': curr = &(v = ""); |
| 323 | break; |
| 324 | default: *curr += c; |
| 325 | } |
| 326 | } |
Mike Klein | 88f9c1e | 2017-03-27 12:43:44 -0400 | [diff] [blame] | 327 | (*this)[k] = v; |
Mike Klein | 7ac0483 | 2017-03-25 11:29:41 -0400 | [diff] [blame] | 328 | } |
| 329 | |
Mike Klein | 88f9c1e | 2017-03-27 12:43:44 -0400 | [diff] [blame] | 330 | std::string& Options::operator[](std::string k) { return this->kv[k]; } |
| 331 | |
Mike Klein | 7ac0483 | 2017-03-25 11:29:41 -0400 | [diff] [blame] | 332 | std::string Options::operator()(std::string k, std::string fallback) const { |
| 333 | for (auto it = kv.find(k); it != kv.end(); ) { |
| 334 | return it->second; |
| 335 | } |
| 336 | return fallback; |
| 337 | } |