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);
}