I write type-safe generic data structures in C
Technique and Overall Reception
- Core idea discussed: use a union field plus
typeof(or compiler extensions) so a generic list “handle” carries element type information, with type safety enforced via function pointer types or dummy assignments. - Many commenters find the trick clever and potentially useful, especially because the macros expand to normal functions that are debuggable and don’t impose per-element runtime overhead.
- Others feel it’s too complex for everyday use and prefer more conventional macro-based generics or just switching to C++.
Intrusive Data Structures and Unions
- Several people note the approach fits non-intrusive containers (e.g., lists where nodes point to data), but intrusive structures (node embedded in user struct, possibly multiple containers per object) are harder to express this way.
- For intrusive structures, commenters often rely on macro-heavy wrappers or Linux-kernel-style embedded list nodes, sometimes erasing types intentionally and accepting runtime checks or casts.
Compiler, C23, and typeof Issues
- Discussion of C23’s structural type equivalence for tagged unions: it helps but only when union tags and layouts match; generating unique tags per instantiation is nontrivial for complex types.
- Long side-thread on
typeofin MSVC: when it appeared, differences in semantics, and bugs/limitations (e.g., function-pointertypeofnot working as documented). - Some criticize function-pointer casting as relying on non-guaranteed pointer representation and potentially breaking aliasing analysis.
Correctness and Practical Limitations
- Technical critiques: alignment and padding concerns with
uint64_t data[]; strict aliasing violations; macro variants that inadvertently overwrite list heads; inability to return values from certain macro forms; double evaluation of arguments. - Concerns that relying on UB-sensitive tricks and aliasing subtleties undermines robustness, even if compilers usually “optimize it away.”
Alternative Approaches to Generics in C
- Widely used alternative: “pseudo-templates” via header macros that generate type-specialized structs and functions per instantiation, trading boilerplate for straightforward codegen and optimization.
- Other schemes: function-pointer–based type carriers; external vtables with forward declarations; intrusive list patterns; elaborate code generators and custom header languages.
- Some argue that for many programs, hand-written, use-case-specific structures (often arrays) suffice and avoid generic complexity.
Why Not Just Use C++ or Another Language?
- Many suggest C++ templates (or D, Rust) as cleaner solutions with language support.
- Counterpoints: entrenched C codebases, embedded targets with limited toolchains, safety/certification constraints, and projects or extension APIs that are “C-only.”
- Philosophical split: some see advanced macro tricks as overengineering; others view them as pragmatic tools when C is mandated.