Type-Safe Wrappers: Macros That Protect Your void*

Post 5 of the Dynamic Arrays in C series · Full source code on GitHub The Ergonomics Problem Post 4 left us with a working generic array. One struct, one push function, one get function, stores any fixed-size type. The void* engine works. But using it is miserable. To push an integer, you can’t just write array_push(arr, 42). You have to create a variable, then pass its address: int x = 42; array_push(arr, &x);. Two lines for something that should be one expression. To read a value back, you get a void* that you must cast manually: int *val = (int *)array_get(arr, 0);. Every access requires a cast. Every cast is an opportunity to lie to the compiler, write double *val = (double *)array_get(arr, 0) on an int array, and the compiler won’t say a word. You’ll get garbage at runtime and spend an hour wondering why. ...

June 8, 2026 · 14 min · pablo

Type Erasure: Generic Arrays with void* and memcpy

Post 4 of the Dynamic Arrays in C series · Full source code on GitHub The Limitation We’ve Been Ignoring For three posts, our array has stored integers. Only integers. If you wanted to store floats, you’d write a FloatArray, same struct, same logic, same push function, just with float instead of int everywhere. If you wanted to store a 20-byte struct, you’d write a StructArray. And another for double. And another for char*. Every new type means a new copy of the same code with the same bugs, the same growth logic, the same visualization, the same tests, just with a different type name. ...

June 3, 2026 · 15 min · pablo