Jackson Pelot

Polymorphism in C

I discover cool features in the C programming language


At the time of writing, I am currently working on a raycast engine for a future video game idea of mine. While I have previous experience programming in C/C++, this is the first large project I have undertaken in pure C. It has been fun to discover the grittier details of the language and new ways of doing things with the minimalistic feature set C provides.

I feel the need to disclaim that I have no doubt in my mind that my revelations in this blog would be obvious to any experienced C developer. Primarily, my goal is to get my thoughts down into writing for my own mental clarity. My secondary goal is to put something out into the internet that could possibly be a breadcrumb for anyone walking down the same path as I am now. Hello! I was here too! You’re going in the right direction.

Polymorphism in general

Polymorphism, as a concept, is the ability in programming to treat different types of objects as the same object. In an object-oriented programming language, this functionality would be provided to the programmer through means such as class inheritance, abstract classes, etc. These language features allow the programmer to define objects hierarchically so that objects defined “underneath” higher objects can be referenced as if they were the higher object.

A great example in my mind is Java’s interface class; It allows the programmer to define a standard interface for how the rest of the program will interact with the object. Then, as long as any object “under” the interface class implements that interface, they can all be interacted with as if they were all the same type of object.

This is extremely powerful, but C is not object-oriented and provides no built-in features to support polymorphism. Is there any way we can pull off something similar in C?

Structs and Unions in C

Writing this, I wanted to include a brief explanation of the struct and union datatypes, but it would have made this post longer than it had any right to be. This is where a baseline understanding of those concepts is going to be necessary to keep following along, so if you’re not familiar with structs and unions, below are two resources I recommend:

Structs and unions are somewhat similar; A struct contains a collection of different data types and a union contains one out of several possible datatypes. Previously, I understood that, and while I fully appreciated the utility of structs, unions were left largely ignored and undervalued.

Structs and Unions Together

For the raycasting engine I am working on, I am using Simple DirectMedia Layer (SDL) as a graphics library. In the process of exploring SDL’s API, I discovered something that didn’t make sense to me.

The details of SDL’s API aren’t important for this post, but it includes a provision to receive an “event” datatype that can represent any kind of action the user can make. Every kind of event has unique information associated with it, so they can’t all share the same data type. In fact, each kind of event has its own struct to organize the data that is relevant to it. The “event” datatype is actually a union of all the different structs for each kind of event it could be. The rest of the program can figure out what kind of event it is and act upon it accordingly.

To some definitions, this is polymorphism. The “event” is a single unit of data that can represent any one of many unique kinds of events. Being new to C, I had not thought to do anything that way, As interesting as the discovery was, I still didn’t understand how the program would be able to figure out what kind of event the “event” type represented. After digging a little deeper, I realized what was happening.

How this is being pulled off comes down to how the event structs and the union combining them is defined. Each struct begins with an integer that represents an event ID. When the union is defined, one of its members is defined as an event ID. This ensures that regardless of the kind of event the union contains, the first 4 bytes of the union will contain an integer that represents the event type. The program can access the union as if there were only an integer stored there even if there is an entire event struct in that memory location, because the event ID is the first value in memory for every single struct.

Why it’s cool

After understanding how this works, it almost felt too obvious to write about in a blog post, but I felt that the sudden understanding was profound enough to warrant discussion. It was my previous understanding that because C didn’t have built-in features to provide polymorphism, it just wasn’t possible entirely. I am pleasantly surprised to have my preconceived notions changed.

Sure, it’s not done for you. OOP languages make this entire process much more automatic, but it’s surprising how easy it is to achieve this sort of pseudo-polymorphism without any object-ness whatsoever.

I already know how this concept can be applied to the development of my raycast engine. If the promises of inheritance and encapsulation could persuade me to switch to development in an OOP, it hasn’t happened yet. For now, this is enough.

#programming #C/C++