Useless attempt to avoid gcc extensions

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?

A scenario under the standard 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.

Friendly code using the extensions

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.

Ugly code under the standard language

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.