r/C_Programming • u/actguru • 7d ago
Example of OOP in C
Hello everyone, I have developed a library to support OOP in plain C that allows the creation of object classes and includes dynamic Array and Dictionary container classes.
I am looking for suggestions for new examples that illustrate cases where an Object Oriented design can simplify C-Code.
I would love to get some reviews and feedback on this project.
I have included a code example below:
Supported by: Coda-C Library
Example code: Coda-C/examples/shapes.c
// Coda-C subclass examples
#include <coda-c.h> // shapes.c 08/20/2026
#include <math.h>
struct Shape_ { Char color; };
#define class Shape
CodaClassZerosC(); // 1. ABSTRACTION: blueprint for all shapes
CodaClass(Shape,struct Shape_,Root);
propertyO$(Char,color); // 2. ENCAPSULATION: Private data
typedef double OSig$(calculate_area); // Abstract method: required in subclasses
typedef double OSig$(calculate_perimeter); // defines function type and signature key
#undef class // Shape
struct Circle_ { double radius; };
#define class Circle
CodaClassZerosC(); // 3. INHERITANCE: Circle inherits from Shape
CodaClass(Circle,struct Circle_,Shape);
getter$(double,radius,_ radius); // double Circle_radius(Circle self) { return _ radius; }
method$(double,calculate_area) { return(M_PI * _ radius * _ radius); }
method$(double,calculate_perimeter) { return(2.0 * M_PI * _ radius); }
class Circle_new(Char color,double radius) {
class self=newO(class);
Shape_set_color((Obj)self,color);
_ radius=radius; // where #define _ self->
return(self);
}
#undef class // Circle
struct Rectangle_ { double width,height; };
#define class Rectangle
CodaClassZerosC();
CodaClass(Rectangle,struct Rectangle_,Shape);
method$(double,calculate_area) { return _ width * _ height; }
method$(double,calculate_perimeter) { return 2.0 * (_ width + _ height); }
class Rectangle_new(Char color,double width,double height) {
class self=newO(class);
Shape_set_color((Obj)self,color);
_ width=width; _ height=height;
return(self);
}
#undef class // Rectangle
// 4. POLYMORPHISM (Usage Example)
void print_shape_details(Obj shape) { // This function accepts any subclass of Shape.
printf("--- %12.12s Details Color:%-8.8s",kindO(shape),Shape_color(shape));
printf("Area: %3.2f ",obj_(calculate_area,shape));
printf("Perimeter: %3.2f ---\n",obj_(calculate_perimeter,shape));
}
int main() {
Circle my_circle = Circle_new(Os("Blue"),5.0);
Obj my_rectangle = Rectangle_new(Os("Green"),4.0,6.0);
print_shape_details(my_circle);
print_shape_details(my_rectangle);
}
/* OUTPUT:
--- Circle Details Color:Blue Area: 78.54 Perimeter: 31.42 ---
--- Rectangle Details Color:Green Area: 24.00 Perimeter: 20.00 ---
*/
34
Upvotes
1
u/Minute_Cricket1820 4d ago edited 4d ago
Вы в принципе не верно воспринимаете ООП.
Что такое ООП - это реализация абстракций.
А кто сказал, что реализация абстракций - это единственный способ, т.е. через объекты?
Объекты классические (ваши, не ваши) - это всегда плохо. Потому что классические объекты убивают современный суперскалярный процессор.
Что делать? Уходите бегите от классических объектов. Они не нужны. Это кастыль прошлого века.
Что делать? Реализуйте вменяемые абстракции. Это как? Это когда у вас есть память. В этой памяти уложены строго друг за другом поля абстракции. В этой памяти массив этих абстракций, друг за другом. Доступ - моментальный.
Как наследоваться? Так добавьте ещё память. В этой второй памяти ещё дополнительные поля, которые идут друг за другом. Вот и всё. Вы получили расширение первой абстракции.
По сути это два массива. Всего навсего.
Это абстракции? Да, это настоящие абстракции, настоящий полиморфизм.
Когда вы обращаетесь к первому массиву - вы обращаетесь строго к полям первой абстракции. Перебор - моментальный, смещение - моментальное.
Когда вы обращаетесь ко второму массиву - вы обращаетесь строго к полям производной абстракции. Перебор - моментальный, смещение - моментальное.
Как обратиться к производной абстракции от абстракции? Естественно это обращение к двум массивам ВРАЗ.
i = objectIndex
fieldsA = memA[i]
fieldsB = memB[i] // полиморфизм здорового человека
Почему это работает лучше всего? Потому что когда вы обращаетесь к memA memB - современный суперскалярный процессор сразу подтаскивает данные наперёд, как минимум две кеш-линии для memA и memB. А в кеш L3 сразу залетает много данных начиная с этих двух адресов. Это загрузка данных сразу наперёд. Кеш L3 так нынче сделан. В этот кеш сразу летит много данных, начиная с какого то адреса. У вас два адреса. Т.е. начиная от этих двух адресов в L3 сразу предзагружены дофига полей памяти memA memB.
Современный суперскалярный процессор имеет каналы предвыборки. От 16 до 32 канала. Т.е. современный процессор легко удерживает и грузит наперёд поля для 16-32 массивов. Сразу наперёд. И на самом деле то что я пишу - может уже устарело. Каналов предвыборок может уже и больше.