News for hackers. Stuff that matters.
Re:C Posix complaint argument parsing in 42 loc, inspired by... by xfennec on Thursday January 05, 2023 @11:51AM (#34262658)
How this code can enter the "if (0)" part?
Re:C Posix complaint argument parsing in 42 loc, inspired by... by cesarb on Thursday January 05, 2023 @12:11PM (#34262965)
The way "switch" works in C is very weird. It behaves more like a "goto", where each "case ...:" is a label to which it can jump.

So when the character is '-', it starts just before the "if (0)" (this part is hidden within the ARG_BEGIN macro), and as you noted, it will never enter that block. However, when the character is 'a', 'b', 'c', or nothing (the end-of-string marker), it will jump directly to the corresponding "case ...:" label, even though it's within that "unreachable" block.

Re:C Posix complaint argument parsing in 42 loc, inspired by... by pcwalton on Thursday January 05, 2023 @11:55AM (#34262720)
A switch can jump inside an if body that contains case labels, causing the condition not to be evaluated.

C switch/case semantics are... not the most obvious.

Re:C Posix complaint argument parsing in 42 loc, inspired by... by mark_undoio on Thursday January 05, 2023 @11:56AM (#34262738)
Because there's really a switch statement (hidden behind a macro) that will jump to labels within that block.

The fact that the if condition is false means it won't just run the whole block straight through but you can still jump to a label in it. A goto statement would also allow you to jump into an otherwise-unreachable block.

Re:C Posix complaint argument parsing in 42 loc, inspired by... by mtlmtlmtlmtl on Thursday January 05, 2023 @11:59AM (#34262789)
ARG_BEGIN opens a switch statement. So it can enter any of the cases, or it'll jump to the else if part.
Re:C Posix complaint argument parsing in 42 loc, inspired by... by kreetx on Thursday January 05, 2023 @12:06PM (#34262907)
Is there any reason this is not written as `switch(..) { <case statements>; default: <else statements> }`. I.e is there a reason for `if(0)`?
Re:C Posix complaint argument parsing in 42 loc, inspired by... by jsmith45 on Thursday January 05, 2023 @01:17PM (#34264053)
The whole `if` portion is for if the second character of the argument (after the initial `-`) is a second `-` [0].

In that case, the switch jumped to `case '-':` hidden within the macro, and the if statement about processing long arguments (like `--help`). arguments only available as a short flag should never get executed as part of a short flag, so putting them inside the if(0) case is an easy option.

An alternative without if(0) of any variety would be:

    ARG_BEGIN {
            if (ARG_LONG("reverse")) case 'r': {
                    reverse = 1;
                    ARG_FLAG();
            } else if (ARG_LONG("input")) case 'i': {
                    input = ARG_VAL();
            } else if (ARG_LONG("output")) case 'o': {
                    output = ARG_VAL();
            } else if (ARG_LONG("help")) case 'h': case '?': {
                    printf("Usage: %s [OPTION...] [STRING...]\n", argv0);
                    puts("Example usage of arg.h\n");
                    puts("Options:");
                    puts("  -a,                set a to true");
                    puts("  -b,                set a to true");
                    puts("  -c,                set a to true");
                    puts("  -r, --reverse      set reverse to true");
                    puts("  -i, --input=STR    set input string to STR");
                    puts("  -o, --output=STR   set output string to STR");
                    puts("  -h, --help         display this help and exit");
                    return EXIT_SUCCESS;
            } else { default:
                    fprintf(stderr,
                            "%s: invalid option '%s'\n"
                            "Try '%s --help' for more information.\n",
                            argv0, *argv, argv0);
                    return EXIT_FAILURE;
            }
            break;
            case 'a': a = 1; ARG_FLAG(); break;
            case 'b': b = 1; ARG_FLAG(); break;
            case 'c': c = 1; ARG_FLAG(); break;
            case '\0': readstdin = 1; break;
    } ARG_END;
  
The downside is that those few short flag only arguments no longer line up nicely with the others, and they come after the default, which could be a little bit confusing.

Footnote: [0] Except if that second dash is the last character of the argument, in which case -- is the end of flags marker, meaning any further arguments that begin with `-` are just funky positional paramaters

Re:C Posix complaint argument parsing in 42 loc, inspired by... by tempodox on Thursday January 05, 2023 @12:05PM (#34262877)
The `switch` statement in `ARG_BEGIN`.