Marcin Jahn | Dev Notebook
  • Home
  • Programming
  • Technologies
  • Projects
  • About
  • Home
  • Programming
  • Technologies
  • Projects
  • About
  • An icon of the Core section Core
    • Programs Execution
    • Stack and Heap
    • Asynchronous Programming
      • Overview
      • Event Queues
      • Fibers
      • Stackless Coroutines
  • An icon of the .NET section .NET
    • HTTPClient
    • Async
      • How Async Works
      • TAP Tips
    • Equality
    • Comparisons
    • Enumerables
    • Unit Tests
    • Generic Host
    • Logging
    • Configuration
    • Records
    • Nullability
    • Garbage Collector
    • IL and Allocations
    • gRPC
    • Source Generators
    • Platform Invoke
    • ASP.NET Core
      • Overview
      • Middleware
      • Razor Pages
      • Routing in Razor Pages
      • Web APIs
      • Filters
      • Identity
      • Validation
      • Tips
    • Entity Framework Core
      • Overview
      • Testing
      • Tips
  • An icon of the Angular section Angular
    • Overview
    • Components
    • Directives
    • Services and DI
    • Routing
    • Observables (RxJS)
    • Forms
    • Pipes
    • HTTP
    • Modules
    • NgRx
    • Angular Universal
    • Tips
    • Standalone Components
  • An icon of the JavaScript section JavaScript
    • OOP
    • JavaScript - The Weird Parts
    • JS Functions
    • ES Modules
    • Node.js
    • Axios Tips
    • TypeScript
      • TypeScript Environment Setup
      • TypeScript Tips
    • React
      • React Routing
      • MobX
    • Advanced Vue.js Features
  • An icon of the Rust section Rust
    • Overview
    • Cargo
    • Basics
    • Ownership
    • Structures
    • Enums
    • Organization
    • Collections
    • Error Handling
    • Generics
    • Traits
    • Lifetimes
    • Closures
    • Raw Pointers
    • Smart Pointers
    • Concurrency
    • Testing
    • Tips
  • An icon of the C/C++ section C/C++
    • Compilation
    • Structures
    • OOP in C
    • Pointers
    • Strings
    • Dynamic Memory
    • argc and argv Visualization
  • An icon of the GTK section GTK
    • Overview
    • GObject
    • GJS
  • An icon of the CSS section CSS
    • Responsive Design
    • CSS Tips
    • CSS Pixel
  • An icon of the Unity section Unity
    • Unity
  • An icon of the Functional Programming section Functional Programming
    • Fundamentals of Functional Programming
    • .NET Functional Features
    • Signatures
    • Function Composition
    • Error Handling
    • Partial Application
    • Modularity
    • Category Theory
      • Overview
      • Monoid
      • Other Magmas
      • Functors
  • An icon of the Algorithms section Algorithms
    • Big O Notation
    • Array
    • Linked List
    • Queue
    • Hash Table and Set
    • Tree
    • Sorting
    • Searching
  • An icon of the Architecture section Architecture
    • What is architecture?
    • Domain-Driven Design
    • ASP.NET Core Projects
  • An icon of the Core section Core
    • Programs Execution
    • Stack and Heap
    • Asynchronous Programming
      • Overview
      • Event Queues
      • Fibers
      • Stackless Coroutines
  • An icon of the .NET section .NET
    • HTTPClient
    • Async
      • How Async Works
      • TAP Tips
    • Equality
    • Comparisons
    • Enumerables
    • Unit Tests
    • Generic Host
    • Logging
    • Configuration
    • Records
    • Nullability
    • Garbage Collector
    • IL and Allocations
    • gRPC
    • Source Generators
    • Platform Invoke
    • ASP.NET Core
      • Overview
      • Middleware
      • Razor Pages
      • Routing in Razor Pages
      • Web APIs
      • Filters
      • Identity
      • Validation
      • Tips
    • Entity Framework Core
      • Overview
      • Testing
      • Tips
  • An icon of the Angular section Angular
    • Overview
    • Components
    • Directives
    • Services and DI
    • Routing
    • Observables (RxJS)
    • Forms
    • Pipes
    • HTTP
    • Modules
    • NgRx
    • Angular Universal
    • Tips
    • Standalone Components
  • An icon of the JavaScript section JavaScript
    • OOP
    • JavaScript - The Weird Parts
    • JS Functions
    • ES Modules
    • Node.js
    • Axios Tips
    • TypeScript
      • TypeScript Environment Setup
      • TypeScript Tips
    • React
      • React Routing
      • MobX
    • Advanced Vue.js Features
  • An icon of the Rust section Rust
    • Overview
    • Cargo
    • Basics
    • Ownership
    • Structures
    • Enums
    • Organization
    • Collections
    • Error Handling
    • Generics
    • Traits
    • Lifetimes
    • Closures
    • Raw Pointers
    • Smart Pointers
    • Concurrency
    • Testing
    • Tips
  • An icon of the C/C++ section C/C++
    • Compilation
    • Structures
    • OOP in C
    • Pointers
    • Strings
    • Dynamic Memory
    • argc and argv Visualization
  • An icon of the GTK section GTK
    • Overview
    • GObject
    • GJS
  • An icon of the CSS section CSS
    • Responsive Design
    • CSS Tips
    • CSS Pixel
  • An icon of the Unity section Unity
    • Unity
  • An icon of the Functional Programming section Functional Programming
    • Fundamentals of Functional Programming
    • .NET Functional Features
    • Signatures
    • Function Composition
    • Error Handling
    • Partial Application
    • Modularity
    • Category Theory
      • Overview
      • Monoid
      • Other Magmas
      • Functors
  • An icon of the Algorithms section Algorithms
    • Big O Notation
    • Array
    • Linked List
    • Queue
    • Hash Table and Set
    • Tree
    • Sorting
    • Searching
  • An icon of the Architecture section Architecture
    • What is architecture?
    • Domain-Driven Design
    • ASP.NET Core Projects

Object-Oriented Programming in C

C is a procedural language, it does not support OOP principles, like inheritance, or polymorphism. There are various approaches to adding OOP into C. All of them are different variants of type punning.

All these approaches are inherently unsafe. The compiler will not catch all of the issues, the program will just explode in the runtime (most likely “Segmentation Fault”).

Using Union

This simple approach allows us to add some kind of polymorphism into C.

Here’s a simple example:

#include <string.h>
enum Type {
PERSON,
STUDENT,
WORKER
};
struct Person {
enum Type type;
int age;
};
struct Student {
enum Type type;
int age;
char faculty[20];
};
struct Worker {
enum Type type;
int age;
char* job;
};
union Object {
struct Person person;
struct Student student;
struct Worker worker;
};
int main() {
union Object object;
object.student.type = STUDENT;
object.student.age = 20;
strcpy(object.student.faculty, "Electrical Engineering");
DoSomething(object);
}
void DoSomething(union Object object) {
switch(object.person.type) {
case PERSON:
// do something...
break;
case STUDENT:
// do something...
break;
case WORKER:
// do something...
break;
}
}

A few points to note:

  • we have a base type (Person) and two “deriving” types: Student and Worker.
  • the base type’s members have to be repeated in every “inheriting” type (in the exact same order!). This is due to the fact that we’re using a union, and the memory layout should be the same in every “derived” type. Any change in the base type requires changes in derived types (unsafe).
  • we can recognize what the actual type is in the runtime via an enum.
  • there’s only one level of inheritance

GTK

GTK Events use this exact approach! The base type looks as follows:

struct GdkEventAny {
GdkEventType type;
GdkWindow* window;
byte sendEvent;
};

Every event has these members repeated, as we’ve seen in the simple example above. The union type is GdkEvent.

Using Parent Member

Let’s look at a different approach that uses composition:

#include <string.h>
enum Type {
PERSON,
UNIVERSITY_WORKER,
PROFESSOR
};
struct Person {
enum Type type;
int age;
};
struct UniversityWorker {
struct Person parent;
char faculty[20];
};
struct Professor {
struct UniversityWorker parent;
unsigned int office_number;
};
int main() {
struct Professor prof;
prof.parent.parent.type = PROFESSOR;
prof.parent.parent.age = 43;
strcpy(prof.parent.faculty, "Electrical Engineering");
prof.office_number = 52;
struct Person* person = (struct Person*)&prof;
DoSomething(person);
}
void DoSomething(struct Person* person) {
switch(person->type) {
case PERSON:
// do something...
break;
case UNIVERSITY_WORKER:
// do something...
break;
case PROFESSOR:
// do something...
break;
}
}

Notes:

  • this time, we have an inheritance chain: Base -> UniversityWorker -> Professor (multiple levels of inheritance).
  • we’re using pointer casting to treat Professor as a Person (unsafe)

GObject

This approach is the basis of how GObject is built. The dirty bits are hidden behind macros that the apps built using GTK utilize.

Resources

  • Dive into GObject (YouTube)
  • Type Punning (Wikipedia)
←  Structures
Pointers  →
© 2023 Marcin Jahn | Dev Notebook | All Rights Reserved. | Built with Astro.