user7893856
user7893856

Reputation: 127

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?

Upvotes: 3

Views: 424

Answers (2)

Vlad Dinev
Vlad Dinev

Reputation: 432

You can do it like this:

#define myclass_members int a;\
                void (*f)(void)

struct myclass {
    myclass_members;
};

#define derived_cls_members myclass_members;\
                            double d;\
                            void (*g)(void)

struct derived_cls {
    derived_cls_members;
};

If you are interested, you can check out a whole framework based on this here.

Edit: turns out this approach may work, but as others have pointed out it's dubious. A better option would be to use the anonymous struct expansion added in c11, which can still be used with the preprocessor as @tgregory pointed out.

#define myclass_members struct { int a; void (*f)(void); }

Edit 2: A further look into this reveals that the first approach (with only the preprocessor) should practically work. So if you are not concerned about the mentioned in the comment assignment issues, or the issues that may come up with using functions like memset()/memcpy(), it should be fine.

Sources:

Are C-structs with the same members types guaranteed to have the same layout in memory?

What is the strict aliasing rule?

Edit 3: To not get something optimized away, you'd want to compile with the -fno-strict-aliasing flag if compiling with optimization.

Bottom line: it's dirty, it's a hack, but should work in practice.

Upvotes: 2

John Bollinger
John Bollinger

Reputation: 180978

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.

Upvotes: 3

Related Questions