3 ms·
As an OOP dev this kind of horrifies me, even though it works okay (though it's surely not thread-safe?) Nevertheless, even in C, I'd find it conceptually simp
by plaguuuuuu 8y ago
As an OOP dev this kind of horrifies me, even though it works okay (though it's surely not thread-safe?)
Nevertheless, even in C, I'd find it conceptually simpler to understand if the functions were written as pure functions, returning a tuple with (c, state) and passing the state back into the caller. Using non-idiomatic tricks and macros in this way is harder to read and understand.
- wahern 8y agoHere's a POSIX compliant portable getopt() routine I wrote which uses this style. Is this hard to read? Note that it's several times shorter than every other implementation I've ever seen, and if I do say so myself I think it's eminently readable. static int u_getopt_r(int argc, char *const argv[], const char *shortopts, struct u_getopt_r *K) { K->optarg = NULL; K->optopt = 0; GETOPT_ENTER; while (K->optind < argc) { K->cp = argv[K->optind]; if (!K->cp || *(K->cp) != '-' || !strcmp(K->cp, "-")) { break; } else if (!strcmp(K->cp, "--")) { K->optind++; break; } for (;;) { char *shortopt; if (!(K->optopt = *++K->cp)) { K->optind++; break; } else if (!(shortopt = strchr(shortopts, K->optopt))) { getopt_err(argc, argv, shortopts, K, "illegal option -- %c\n", K->optopt); GETOPT_YIELD('?'); } else if (shortopt[1] != ':') { GETOPT_YIELD(K->optopt); } else if (K->cp[1]) { K->optarg = &K->cp[1]; K->optind++; GETOPT_YIELD(K->optopt); break; } else if (K->optind + 1 < argc) { K->optarg = argv[K->optind + 1]; K->optind += 2; GETOPT_YIELD(K->optopt); break; } else { getopt_err(argc, argv, shortopts, K, "option requires an argument -- %c\n", K->optopt); K->optind++; GETOPT_YIELD((*shortopts == ':')? ':' : '?'); break; } } } GETOPT_LEAVE; return -1; } Here's macro magic, the complexity of which is more than made up for by the readability of the core code: #define GETOPT_ENTER \ do { \ static const int pc0 = __LINE__; \ switch (pc0 + K->pc) { \ case __LINE__: (void)0 #define GETOPT_SAVE_AND_DO(do_statement) \ do { \ K->pc = __LINE__ - pc0; \ do_statement; \ case __LINE__: (void)0; \ } while (0) #define GETOPT_YIELD(rv) \ GETOPT_SAVE_AND_DO(return (rv)) #define GETOPT_LEAVE \ GETOPT_SAVE_AND_DO(break); \ } \ } while (0)