Dervin Thunk
Dervin Thunk

Reputation: 20139

What were the C idioms for polymorphism and inheritance before the concepts were made explicit?

Suppose I have the usual class Animal abstract class and the class Dog : public Animal class Cat : public Animal that makes it a concrete class you can instantiate an object from. Suppose further that you have a function foo(Animal a), taking either cats or dogs as objects. C++ used to, in the early days, compile to C, and would build a vTable maintaining the objects there.

But a student of mine asked me this question: before these concepts were usual talk among programmers, how did they actually do it in their day-to-day coding in C? What was (is?) the idiomatic way to program these concepts in C?

I've sifted through the linux kernel, and other OSS projects, but I haven't been able to find a clear pattern: sometimes it's unions (for different structs), sometimes it's function pointers, etc. but I would like a straight answer from knowledgeable people in industry who've done and have a lot of experience with C.

In one sentence: what is idiomatic C for inheritance and polymorphism?

Upvotes: 10

Views: 761

Answers (3)

Jiminion
Jiminion

Reputation: 5164

I saw code that used #defines to create abstract names in the source and include files. for example, the same routine might be re-used to deal with bytes, shorts, ints, floats, and doubles. The code was messy and hard to understand.

Upvotes: 0

Tarik
Tarik

Reputation: 11209

Before the advant of object oriented programming, applications were using procedural code where functionality was "encapsulated" in a hierarchy of functions with the ones at the top of the hierarchy working with abstractions and becoming more and more concrete and detailed as we go down the hierarchy. Some API provide ways to create structures that can only be manipulated via handles. Example: In C, file handles hide the details of dealing with files.

As for doing OO in C, I believe it was never a viable option without proper language support with the appropriate constructs. What you would get was not worth the pain involved in faking OO with pointers to functions.

Upvotes: -1

user4815162342
user4815162342

Reputation: 155286

Simple programs, such as those written for school assignments, implement polymorphism using a structure that consists of a union and optionally an enum as the type discriminators. Each "method" then contains a switch statement that calls into the function appropriate for the subtype. Obviously this doesn't scale to more systems that require being able to add subclasses without changing the definition of the base class.

Polymorphism itself is easily expressed with function pointers that receive an explicit self argument. Open-ended inheritance can be achieved with the "inherited" structure embedding its superclass:

struct base {
   // ... members here ...
};

struct inherited {
  struct base base;
  // ... inherited members here ...
};

Pointers to struct inherited may be safely cast to struct base *, a practice explicitly blessed by the C standard. These casts are typically hidden behind macros, which may even perform runtime type checking, where possible.

Implementing this is quite unwieldy, since there are no templates, no automatic invocation of destructors, no exceptions, and no STL. In other words, error handling and destructor invocation must be carefully handled by the programmer, and type variance must be handled either by callbacks at run-time (consider the difference between std::sort() and qsort()) or by hard to maintain preprocessor tricks.

Despite the difficulties, it is certainly possible to implement a meaningful subset of C++ functionality in C, and even a semblance of simplicity of C in the process. To study real-world examples of this approach being taken to production level, take a look at the implementation of the CPython interpreter, or the glib object system used by GTK+.

Upvotes: 8

Related Questions