Where are [compatible_types and typenameof] useful in C?
typenameof (expression) would correspond to C
STRINGIFY(typeof(expression)) if it was supported and actually worked. Essentially, it allows a library to implement run-time interfaces like
#define register_type(expr) lib_register_type(typenameof (expr), sizeof (expr)) mytype_id = register_type(type-or-expression);for registering a type dynamically, for example in a message passing or event library, and
#define send(handle, obj) lib_send((handle), &(obj), sizeof (obj), typenameof (obj)) result = send(handle, object);to send not just the storage representation of an object, but also its type name and size, so the recipient knows what it is receiving.
Currently, the two interfaces have to be implemented as something like
mytype_id = register_type("typename", size); result = send(handle, pointer, size, "typename");which are prone to bugs (especially bitrot), because there is no way to check that
typename actually matches the type, for example when due to a bugfix or similar the original type is modified, but not all places where its type is mentioned are correctly updated to reflect the change.
compatible_types matches the GCC/Clang/Intel CC
__builtin_compatible_types_p() C extension. If you support macros, it allows you to do type inference with a compile-time fixed set of types, for example in C,
#define TYPECODE(type) (__builtin_compatible_types_p(type, uint8_t) ? TYPE_U8 : \ __builtin_compatible_types_p(type, int8_t) ? TYPE_I8 : \ __builtin_compatible_types_p(type, uint16_t) ? TYPE_U16 : \ __builtin_compatible_types_p(type, int16_t) ? TYPE_I16 : \ __builtin_compatible_types_p(type, uint32_t) ? TYPE_U32 : \ __builtin_compatible_types_p(type, int32_t) ? TYPE_I32 : \ __builtin_compatible_types_p(type, uint64_t) ? TYPE_U64 : \ __builtin_compatible_types_p(type, int64_t) ? TYPE_I64 : TYPE_UNKNOWN)Unlike comparing typenames, on architectures where
char,
short,
int,
long, and
long long match the above specific-size integer types (they likely shall on all in C23), they too will match to the known types using the above macro.
With this, the type of an expression can be detected and specified at runtime, for example when a compile-time fixed set of types are supported by a library, but the user of that library wants to use their own type names (say,
u16 and such).
Note that
!strcmp(typenameof (a), typenameof (b)) is not the same as
__builtin_compatible_types_p(typeof(a), typeof(b)). If we consider
typedef a b;, the former yields
false (0), whereas the latter yields
true (1).
Most important use cases for both are about not having to specify the type of an object or an expression, when already supplying that object or expression, because re-stating the type is prone to bugs. One might think that these are corner cases, but fact is, such bugs are unfortunately common, and could be completely avoided with these two compile-time keywords.
The only thing that annoys me is that the linker does not yet know how to sort those entries at link time.
There is SORT_BY_NAME (or simply SORT) keyword for input section description, does it not work?
I was referring there to sorting the
contents of a section, treating it as an array of
N structures of size
L with a sort key of type
T at offset
K.
I guess one could use custom dotted name for each entry (
.sectionname.key), but it'd only work for keys that are valid section names.
SORT_BY_INIT_PRIORITY works for
.sectionname.nonneg section names, where
nonneg is a nonnegative integer between 0 and 65535, inclusive.
(I haven't even found a way yet to generate unique section name suffixes that work (are unique) across source and object files.)
The reason it would be nice for the linker to be able to do this for a small number of key types, is to keep the darn relocation entries in sync with the contents.
I know, I should just stop whining, and go do it portably using bfd (part of binutils) or libelf (non-binutils ELF library).