Comment by glouwbug

16 hours ago

Combined with C23's auto (see vec_for) you can technically backport the entirety of C++'s STL (of course with skeeto's limitation in his last paragraph in mind). gcc -std=c23. It is a _very_ useful feature for even the mundane, like resizable arrays:

  #include <stdlib.h>
  #include <stdio.h>
  
  #define vec(T) struct { T* val; int size; int cap; }
  
  #define vec_push(self, x) {                                                 \
      if((self).size == (self).cap) {                                         \
          (self).cap = (self).cap == 0 ? 1 : 2 * (self).cap;                  \
          (self).val = realloc((self).val, sizeof(*(self).val) * (self).cap); \
      }                                                                       \
      (self).val[(self).size++] = x;                                          \
  }
  
  #define vec_for(self, at, ...)             \
      for(int i = 0; i < (self).size; i++) { \
          auto at = &(self).val[i];          \
          __VA_ARGS__                        \
      }
  
  typedef vec(char) string;
  
  void string_push(string* self, char* chars)
  {
      if(self->size > 0)
      {
          self->size -= 1;
      }
      while(*chars)
      {
          vec_push(*self, *chars++);
      }
      vec_push(*self, '\0');
  }
  
  int main()
  {
      vec(int) a = {};
      vec_push(a, 1);
      vec_push(a, 2);
      vec_push(a, 3);
      vec_for(a, at, {
          printf("%d\n", *at);
      });
      vec(double) b = {};
      vec_push(b, 1.0);
      vec_push(b, 2.0);
      vec_push(b, 3.0);
      vec_for(b, at, {
          printf("%f\n", *at);
      });
      string c = {};
      string_push(&c, "this is a test");
      string_push(&c, " ");
      string_push(&c, "for c23");
      printf("%s\n", c.val);
  }

What I don't quite get is why they didn't go all the way in and basically enabled full fledged structural typing for anonymous structs.

  • That way my plan, but the committee had concerns about type safety.

    • This would probably need some special rules around stuff like:

         typedef struct { ... } foo_t;
         typedef struct { ... } bar_t;
         foo_t foo = (bar_t){ ... };
      

      i.e. these are meant to be named types and thus should remain nominal even though it's technically a typedef. And ditto for similarly defined pointer types etc. But this is a pattern regular enough that it can just be special-cased while still allowing proper structural typing for cases where that's obviously what is intended (i.e. basically everywhere else).

      1 reply →