Stuff on software

Posted on Jan 11, 2025

Language C11, features #define _POSIX_C_SOURCE 200809L. Let’s say we have a data structure:

typedef struct pill_t   pill_t;

struct pill_t {
  int   N;
};

we have some methods:

void
pill_print (pill_t P)
{
  dprintf(1, "pill.N = %d\n", P.N);
}

we can use the data structure in two ways:

void
pill_use_for_weight_loss (pill_t P)
{
  dprintf(1, "weight loss pill.N = %d\n", P.N);
}
void
pill_use_for_lower_blood_sugar (pill_t P)
{
  dprintf(1, "lower blood sugar pill.N = %d\n", P.N);
}

so let’s use it:

int
main (void)
{
  pill_t        P1 = { .N = 123 };
  pill_t        P2 = { .N = 456 };

  dprintf(1, "just a pill_t\n");

  pill_print(P1);
  pill_print(P2);

  pill_use_for_weight_loss(P1);
  pill_use_for_lower_blood_sugar(P2);

  exit(EXIT_SUCCESS);
}

it is just one type: the pill_t.

Now let’s say we start thinking, twisting, craving for light from the Secret Fire, but there is Darkness everywhere, and it corrupts… it torments… it festers; how cool would it be if we could specify the use of the pill in its variable declaration? Like this:

int
main (void)
{
  weight_loss_pill_t            P1 = { .P.N = 123 };
  lower_blood_sugar_pill_t      P2 = { .P.N = 456 };

  dprintf(1, "subtypes of pill_t\n");

  pill_print(P1);
  pill_print(P2);
  pill_use(P1);
  pill_use(P2);
  exit(EXIT_SUCCESS);
}

instead of selecting the use in the name of the function, we select it in the name of the data type; we are not really changing the internal representation of a pill, we are just changing what we write to use pills. Is this new code better? More readable? Can we understand it faster when we read it again after 5 years of not seeing it?

Well… but can we actually do it? Of course:

typedef struct pill_t                   pill_t;
typedef struct lower_blood_sugar_pill_t lower_blood_sugar_pill_t;
typedef struct weight_loss_pill_t       weight_loss_pill_t;

struct pill_t {
  int   N;
};

struct lower_blood_sugar_pill_t {
  pill_t        P;
};

struct weight_loss_pill_t {
  pill_t        P;
};

void
pill_p_print (pill_t P)
{
  dprintf(1, "pill.N = %d\n", P.N);
}

void
lower_blood_sugar_pill_print (lower_blood_sugar_pill_t P)
{
  pill_p_print(P.P);
}

void
weight_loss_pill_print (weight_loss_pill_t P)
{
  pill_p_print(P.P);
}

#define pill_print(PILL) \
  (_Generic((PILL), \
            lower_blood_sugar_pill_t:   lower_blood_sugar_pill_print,           \
            weight_loss_pill_t:         weight_loss_pill_print)(PILL))

void
pill_use_for_weight_loss (weight_loss_pill_t P)
{
  dprintf(1, "weight loss pill.N = %d\n", P.P.N);
}

void
pill_use_for_lower_blood_sugar (lower_blood_sugar_pill_t P)
{
  dprintf(1, "lower blood sugar pill.N = %d\n", P.P.N);
}

#define pill_use(PILL) \
  (_Generic((PILL), \
            lower_blood_sugar_pill_t:   pill_use_for_lower_blood_sugar,         \
            weight_loss_pill_t:         pill_use_for_weight_loss)(PILL))

notice how much more code there is; but all this code is hidden in some pill.h/pill.c module, so we do not see it; we just have to write it and forget it.

I have let code of the second type enter my project’s repository; I’m torn between light and darkness; but… which one is light… which one is darkness…

Am I a Slave of Morgoth?

No… No… That cannot be!!!