I'm wondering is it reasonable, or worthwhile, overriding header declarations in order to set a particular type. In preference to something like void * which adds no type safety.
For example, if you have a generic storage function that adds a sensor reading to a circular buffer:
int add_reading(void *);
In order to be generic, the function has to be defined as a void *. However, in the header file, you could declare the function as:
int add_reading(my_reading_t *);
which would add a degree of type safety over the void pointer. In a generic header, you could set the type with a #define that defaulted to void. Thus the override type could be defined just before the #include.
It seems an unnecessary hack, but one could argue the same for opaque pointers too - using opaque_type_t * in preference to void *. But that at least is defined behaviour. What I wonder is if this type of messing invokes UB (undefined behaviour)?
A function declared with int add_reading(void *)
is not compatible with a function defined with int add_reading(my_reading_t *)
. The behavior of calling a function defined with the latter using an identifier declared with the former (or other function designator with that type) will not be defined by the C standard, per C 2018 6.5.2.2 9:
If the function is defined with a type that is not compatible with the type (of the expression) pointed to by the expression that denotes the called function, the behavior is undefined.
Per 6.7.6.1 2:
For two pointer types to be compatible, both shall be identically qualified and both shall be pointers to compatible types.
Obviously, the parameter types void *
and my_reading_t *
are not pointers to compatible types (presuming my_reading_t
is a structure type, is not an alias for void
).
Per 6.7.6.3 15:
For two function types to be compatible,… corresponding parameters shall have compatible types…