How to Write Object-Oriented C Language?

In embedded system development, the C language dominates due to its proximity to hardware, efficiency, and portability. Although C is a procedural programming language, we can fully implement the core features of Object-Oriented Programming (OOP) through some programming techniques: encapsulation, inheritance, and polymorphism.

Traditional C code has limitations, such as difficulty in managing global state, lack of data encapsulation, challenges in achieving polymorphic behavior, and poor code reusability.

For example

// Traditional modular C code
// led.h
void LED_Init(void);
void LED_On(uint8_t led_id);
void LED_Off(uint8_t led_id);

// button.h  
void Button_Init(void);
uint8_t Button_IsPressed(uint8_t button_id);

// Usage
LED_Init();
Button_Init();
LED_On(0);

Can be transformed into an object-oriented approach

// Object-oriented style C code
Led* led1 = Led_Create(0);
Button* button1 = Button_Create(0);

led1->On(led1);
if (button1->IsPressed(button1)) {
    // Handle button press
}

This way, data is bound to operations, and a unified interface can be provided for different implementations, along with clear code organization, making it easy to extend and combine.

Using C language to simulate object-oriented programming mainly utilizes structures and function pointers, including features of inheritance and polymorphism, which will be briefly introduced below.

Encapsulation: Structures and Function Pointers

// shape.h - Base class for shapes
#ifndef SHAPE_H
#define SHAPE_H
#include <stdint.h>

// Forward declaration
typedef struct Shape Shape;

// Shape structure definition
struct Shape {
    // Attributes
    int16_t x;
    int16_t y;
    // Virtual function table (function pointers)
    void (*Draw)(const Shape* self);
    uint32_t (*GetArea)(const Shape* self);
    void (*Destroy)(Shape* self);
    // Private data (information hiding via void pointer)
    void* private_data;
};

// Constructor and public methods
Shape* Shape_Create(int16_t x, int16_t y);
void Shape_Move(Shape* shape, int16_t dx, int16_t dy);
void Shape_PrintInfo(const Shape* shape);
#endif

Inheritance: Implementation of Derived Classes

// rectangle.h - Rectangle class inherits from Shape
#ifndef RECTANGLE_H
#define RECTANGLE_H
#include "shape.h"

// Forward declaration
typedef struct Rectangle Rectangle;

// Rectangle structure definition
struct Rectangle {
    Shape base;  // Base class as the first member (important!)
    // Properties unique to derived class
    uint16_t width;
    uint16_t height;
};

// Constructor
Rectangle* Rectangle_Create(int16_t x, int16_t y, uint16_t width, uint16_t height);
// Methods unique to derived class
void Rectangle_SetSize(Rectangle* rect, uint16_t width, uint16_t height);
float Rectangle_GetAspectRatio(const Rectangle* rect);
#endif

Polymorphism: Unified Interface Calls

// circle.h - Circle class
#ifndef CIRCLE_H
#define CIRCLE_H
#include "shape.h"

typedef struct Circle Circle;

struct Circle {
    Shape base;  // Base class
    uint16_t radius;
};

Circle* Circle_Create(int16_t x, int16_t y, uint16_t radius);
float Circle_GetCircumference(const Circle* circle);
#endif

Although it requires some additional programming discipline and memory management considerations, the advantages of this programming style far outweigh its overhead in complex embedded systems. By effectively utilizing structures, function pointers, and well-designed interfaces, we can enjoy many benefits of object-oriented programming in C while maintaining the performance and efficiency advantages of the C language.

‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧  END  ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧


Follow my WeChat public account, reply "planet" to join the knowledge planet, and get answers to your questions.


Click "Read the original text" to view the details of the knowledge planet. Feel free to share, bookmark, like, and view.

Leave a Comment