Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Emulating inheritance with C preprocessor

I had an idea about how to implement interfaces in C. The idea is really simple, but when searching for questions discussing this, I did not see it mentioned.

So, the "standard" inheritance emulation method in C is essentially

struct dad {
   arm_t right_arm;
   arm_t left_arm;
}

struct kid {
   struct dad parent;
   diaper_t diaper;
}

Which I think is ugly, because kid.parent.right_arm just makes less sense than kid.right_arm. There is another way apparently, if you don't mind using gcc-exclusive flags, which is something like

struct kid {
    struct dad;
    diaper_t diaper;
}

but this is not portable. What is portable, however, is

// file: dad.interface
arm_t right_arm;
arm_t left_arm;

// file: kid.h
struct kid {
    #include dad.interface
    diaper_t diaper;
}

For the purposes of this question, I'm not interested in suggestions for alternatives. My question is simply this: what, if anything, is wrong with this approach?

like image 904
user7893856 Avatar asked Nov 26 '25 23:11

user7893856


1 Answers

Since you pose the question in the context of inheritance, I suppose a more complete (and correct) example would be this:

file: dad.interface

arm_t right_arm;
arm_t left_arm;

file dad.h

struct dad {
    #include "dad.interface"
};

file: kid.h

struct kid {
    #include "dad.interface"
    diaper_t diaper;
};

There are several problems with this, among them:

  1. It's not really inheritance -- it's merely two structures that happen to have matching initial members. struct kid has no other relationship with struct dad, and the former certainly does not inherit anything from the latter. From struct kids perspective, it's not even clear that a struct dad exists.

  2. Although you did not use the term, it is common (albeit unwarranted) for people to assume that polymorphism goes hand-in-hand with inheritance. You don't get that with your approach. Despite sharing a common initial sequence of members, the two structure types are incompatible, and pointers to those types are incompatible, as that term is used by the standard. In particular, accessing a struct kid via a pointer of type struct dad * violates the strict aliasing rule, and therefore produces undefined behavior. (The same does not apply when struct kid embeds a struct dad as its first member.)

  3. The approach has poor extensibility. For example, suppose you want to add struct son and struct daughter inheriting from struct kid. Now you have to pull out struct kid's members into its own separate header, too. I guess you could do that preemptively if you can predict which structure types anyone might ever want to inherit from, or if you just do it to every single structure type. Yikes, what a mess.

like image 105
John Bollinger Avatar answered Nov 29 '25 14:11

John Bollinger



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!