1. 4
  1.  

  2. 15

    I feel like I’m missing something here – why don’t they just use an enum for the type tag and use the union, rather than casting? I use tagged unions quite a lot in C, especially combined with C99 designated initializer syntax, and it doesn’t require any macros or casts.

    #include <stdio.h>
    
    enum some_tag {
        SOME_ONE,
        SOME_TWO,
    };
    
    struct some_one {
      int x;
      int y;
    };
    
    struct some_two {
      float z;
      float w;
    };
    
    struct some {
        enum some_tag tag;
        union {
            struct some_one one;
            struct some_two two;
        } u;
    };
    
    int main(int argc, char **argv) {
        // could also use a designated initializer for a literal:
        /* struct some a = {
         *     .tag = SOME_TWO,
         *     .u.two = {
         *         .w = 5.0,
         *     },
         * }; */
    
        struct some a = {};
        a.tag = SOME_TWO;
    
        printf("Is `a` tagged as SOME_TWO: %d\n", a.tag == SOME_TWO);
    
        struct some *b = &a;
        b->u.two.w = 5.0;
    
        printf("b->w: %f\n", b->u.two.w);
        return 0;
    }
    

    The let part in that gist depends on __auto_type, which appears to be a GNU C11 extension.

    1. 4

      I wondered the same. I’m guessing this code was ripped out of another context because it has defines for var and let not used and also seem terrible to me.

      1. 3

        And this way you don’t need to do fishy arithmetic on pointers.

        C11 also allows you to leave the union anonymous.

        1. 2
          #define var __auto_type
          #define let __auto_type const
          

          It appears that the author is trying to make something look like ES6 (noting the definitions of var and let).

          Also, a really good concrete example for tagged-unions in C is to be found in most implementations of input events.

          1. 1

            The OPs version of tagged unions allocates a struct for each type in the union and never uses it other than to have a unique pointer for each type. This works ok when the number of different possible types is smallish and the size of all structs is smallish. It would really suck if you had an bunch of massive structs that were permanently occupying ram