Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

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.



Here'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)




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: