I am writing a math library and want to have a call like "add" which is generic, takes two parameters v1 and v2, and calls the right function. If v1 is vec2 and v2 is vec2 then it will call vec2_add, if v1 is vec2 and v2 is float it will call vec2_add_float. But if v1 is vec3 and v2 is vec3 it will call vec3_add etc... A small graph is drawn on the bottom
v1 -> vec2, v2-> vec2: call vec2_add v1 -> vec2, v2-> float: call vec2_add_float
v1 -> vec3, v2-> vec3: call vec3_add v1 -> vec3, v2-> float: call vec3_add_float
I did write a small generic code like so:
#define add(v1, v2) _Generic((v1), \
vec2: _Generic((v2), \
vec2: vec2_add, \
float: vec2_add_float \
), \
svec3: _Generic((v2), \
vec3: vec3_add, \
float: vec3_add_float \
) \
)(v1, v2)
For some reason, this works with add_float types but not when I try to add vec2 to vec2 or vec3 to vec3, gives me the error message:
‘_Generic’ selector of type ‘vec2’ is not compatible with any association
30 | vec3: _Generic((v2), \
What am I doing wrong here?
The problem is caused by a peculiar feature of "generic selection" that all selection expressions must be valid. When v2
is vec2
then expression _Generic(v2, vec3: vec3_add, float: vec3_add_float)
is invalid because neither vec3
nor float
is compatible with vec2
.
IMO, this is a serious design flaw of generic selection.
The workaround is using default
to handle the common case which would be vec2_add
is v1
is vec2
, or vec3_add
if v1
is vec3
.
#define add(v1, v2) _Generic((v1), \
vec2: _Generic((v2), \
default: vec2_add, \
float: vec2_add_float \
), \
vec3: _Generic((v2), \
default: vec3_add, \
float: vec3_add_float \
) \
)(v1, v2)