Posted on Wed Mar 1, 2017
March is here. Along with my allergy. Yay!
The gnu C Compiler offers extensions to the C language; among them the -fplan9-extensions allow special handling of the first anonymous field of a data structure. Is it possible to mimic what the -fplan9-extensions do with the facilities of the standard C11 language?
Let’s say we have the following data structures hierarchy:
typedef struct alpha_t { int X; } alpha_t; typedef struct beta_t { alpha_t parent; int Y; } beta_t; typedef struct gamma_t { beta_t parent; int Z; } gamma_t;
to extract a pointer to beta_t
or alpha_t
from a pointer to
gamma_t
we have to do:
gamma_t g = { .parent.parent.X = 0, .parent.Y = 1, .Z = 3 }; gamma_t * G = &g; beta_t * B = &(G->parent); alpha_t * A = &(G->parent.parent); fprintf(stderr, "X=%d, Y=%d, Z=%d\n", A->X, B->Y, G->Z);
because, under the C language, a pointer to gamma_t
is not “formally” a
pointer to beta_t
or alpha_t
.
Now let’s switch to C11 with the -fplan9-extensions; we define the structure types as follows:
typedef struct alpha_t { int X; } alpha_t; typedef struct beta_t { alpha_t; int Y; } beta_t; typedef struct gamma_t { beta_t; int Z; } gamma_t;
to extract a pointer to beta_t
or alpha_t
from a pointer to
gamma_t
we can do:
gamma_t g = { .X = 0, .Y = 1, .Z = 3 }; gamma_t * G = &g; beta_t * B = G; alpha_t * A = G; fprintf(stderr, "X=%d, Y=%d, Z=%d\n", A->X, B->Y, G->Z);
because, under this extended C language, a pointer to gamma_t
is “formally”
a pointer to beta_t
and also a pointer to alpha_t
.
I’m a weak thinker, so this feature really pleases me. But avoiding non–standard compiler–specific extensions would be better.
What could we do with the facilities of C11? We have _Generic
dispatching.
Let’s define the types with named fields:
typedef struct alpha_t { int X; } alpha_t; typedef struct beta_t { alpha_t parent; int Y; } beta_t; typedef struct gamma_t { beta_t parent; int Z; } gamma_t;
now we define cast functions for alpha_t
:
__attribute__((pure,nonnull(1),returns_nonnull,always_inline)) inline alpha_t * my_alpha_from_alpha (alpha_t * A) { return A; } __attribute__((pure,nonnull(1),returns_nonnull,always_inline)) inline alpha_t * my_alpha_from_beta (beta_t * B) { return &(B->parent); } __attribute__((pure,nonnull(1),returns_nonnull,always_inline)) inline alpha_t * my_alpha_from_gamma (gamma_t * G) { return my_alpha_from_beta(&(G->parent)); }
and for beta_t
:
__attribute__((pure,nonnull(1),returns_nonnull,always_inline)) inline beta_t * my_beta_from_beta (beta_t * B) { return B; } __attribute__((pure,nonnull(1),returns_nonnull,always_inline)) inline beta_t * my_beta_from_gamma (gamma_t * G) { return &(G->parent); }
finally we define the cast macros with _Generic
dispatching:
#define my_alpha(SELF) _Generic((SELF), \ alpha_t *: my_alpha_from_alpha, \ beta_t *: my_alpha_from_beta, \ gamma_t *: my_alpha_from_gamma)(SELF) #define my_beta(SELF) _Generic((SELF), \ beta_t *: my_beta_from_beta, \ gamma_t *: my_beta_from_gamma)(SELF)
so we can do this:
gamma_t g = { .parent.parent.X = 0, .parent.Y = 1, .Z = 3 }; gamma_t * G = &g; beta_t * B = my_beta(G); alpha_t * A = my_alpha(G); fprintf(stderr, "X=%d, Y=%d, Z=%d\n", A->X, B->Y, G->Z);
We can define functions as follows:
void alpha_doit (alpha_t * A) { fprintf(stderr, "%s %d\n", __func__, A->X); } void beta_doit (beta_t * B) { fprintf(stderr, "%s %d\n", __func__, B->Y); } #define Alpha_doit(SELF) alpha_doit(my_alpha(SELF)) #define Beta_doit(SELF) beta_doit(my_beta(SELF))
and call them with:
gamma_t g = { .parent.parent.X = 0, .parent.Y = 1, .Z = 3 }; gamma_t * G = &g; Alpha_doit(G); Beta_doit(G);
We can do it, but there is a lot to write. Ideally, a lot of this code would be automatically generated. I cannot say now if it would be worthy.