Type-erased generic functions for C: A modest non-proposal

submited by
Style Pass
2024-09-30 01:00:04

Although C is not really the "portable assembly language" many want it to be, one function definition in C generally leads to one symbol in an object file, and having access to function bodies after they've been compiled once is unnecessary (although helpful for optimization), and it would be good to retain these properties. A less-trodden path for implementing parametric polymorphism in systems-ish languages, used by Swift and also proposed here in N3212, is to use runtime metadata to handle generic types dynamically. N3212 proposes a new _Type primitive type, which carries some metadata about a type's size and other properties, so that other values can be declared with the type represented by that _Type using the _Var specifier: void sort(_Type T, size_t N, _Var(T) array[N], int (*compare)(const _Var(T)*, const _Var(T)*)) { qsort(array, N, sizeof(_Var(T)), compare); } One challenge specifying a language-integrated metadata system is that it needs to provide enough information to serve any possible use that the language supports, even if a particular interface only needs a subset of that metadata. A traditional qsort function only needs the size of the elements to do its work, but other generic functions might want to know a type's alignment, its name, how values of the type interact with the function calling convention, the specifier to use in printf, and so on. The vast body of existing C code uses all sorts of different idioms for ad-hoc polymorphic interfaces, so a language design that prescribes a single metadata mechanism is going to have a tough time finding a balance between providing enough metadata to support a wide range of use cases and not bloating code generation with too much metadata that most clients don't need.

So that's why I'd like to explore a third approach that has the potential to be simpler to understand and implement, more flexible, and I think more in the subjective "spirit of C" than C++ style monomorphization or the proposed N3212 design. I think the combination of type-erased generics, paired with a default argument mechanism to collect situationally-appropriate metadata at call sites, could provide a surprising amount of expressivity, allow for better type checking to be retrofitted to existing C interfaces. Disclaimer To be up front, my language designer duties are focused elsewhere, and I'm not going to formally propose this to the C working group any time soon. As such, I'm only going to describe the broad strokes of a design direction here without working out all the details. I'm strictly in "ideas guy" mode here. If you want to stop reading, that's fine. But if it inspires you to actually implement and/or propose something like this, then great!

Leave a Comment