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.