OOP mimicry in C !!!

Date: 2024/07/30 (Tue)

I am recently programming my own SDL game engine in the PRETENTIOUS language, C, but what should we do if there's no object oriented syntax in it ? Then it must be cool to make it ourselves. Or we can think in this way: let's see Python, C++, Perl or what ever OO language. They are all written in C. So don't listen to people complaining about C not having OO. It can absolutely be done.

Private and Member Functions

  • private variables: underscore naming convention int _value;
  • private functions: static functions with one class per file, yeah, just like Java
  • memeber functions: pass a self pointer this in functions like the fanciest language, Python
        typedef example {
            int _width;
            int _height;
        } example_t;
    
        // private function
        static int calculate_area(example_t* this) {
            return this->_width * this->_height;
        }
        // public function / encapsulation
        int example_get_width(example_t* this) {
            return this->_width;
        }
    
    • the usage of a member function:
        example_t ex;
        int width = example_get_width(&ex);
    
    • or even better, more concise with a function pointer variable and designated initializer.
        typedef example {
            int _width;
            int _height;
            // function pointer
            int (*get_width)(struct example*);
        } example_t;
    
        int example_get_width(example_t* this) {
            return this->_width;
        }
        // usage
        example_t ex = {
            ._width = w,
            ._height = h,
            .get_width = example_get_width
        };
        int width = ex.get_width(&ex);
    
    • we can further make the function static to have shorter name (without the class name prefix) and get rid of the naming collision with stuffs in other header files. And then assign it in a constructor, where we will later benefit from it while doing inheritance.
        // make it private
        static int get_width(example_t* this) {
            return this->_width;
        }
        // then assign in constructor
        example_constructor(example_t* this) {
            this->get_width = get_width;
        }
        // usage
        example_t ex;
        example_constructor(&ex);
        int width = ex.get_width(&ex);
    

Inheritance

  • Just include base class in derived class, and note that it must be the first member. Since the memory alignment mechanism of struct in C, by this way we can directly cast the derived_class_t to base_class_t without any additional manipulation.
        typedef struct derived_class {
            base_class_t base;
            // additional variables
            int _var1;
            int _var2;
        } derived_class_t;
    

Polymorphism

  • overriding
    • cast with function pointer
    • note that: without definition, we can also have a pure virtual function / abstract.
        typedef struct base_class {
            int _var1, _var2;
            // virtual function
            void (*func1)(int, int);
        } base_class_t;
    
        typedef struct derived_class {
            // have to be the first one
            base_class_t base;
    
            // additional variables
            int _var3, _var4;
    
            // functions
            void (*func1)(int, int);
        } derived_class_t;
    
    • we have to have a constructor function to cast the base_class_t::func1 to derived_class_t::func1.
        void derived_class_constructor(derived_class_t* this) {
            // overriding
            this->func1 = this->base.func1;
        }
    
        // usage:
        derived_class_t derived;
        derived->func1((base_class_t*)&derived_class);
    
    • what if we don't like manipulating the type casting of base class when using the derived one? just wrap it more with a static function
        static void derived_class_func1(derived_class_t* this) {
            this->base.func1(&this->base);
        }
        void derived_class_constructor(derived_class_t* this) {
            // overriding
            this->func1 = this->derived_class_func1;
        }
    
        // usage:
        derived_class_t derived;
        derived->func1(&derived_class);
    
  • private virtual function
  • too long ? try using MACRO to simplify into C++ syntax
    #include "logging.h"
    #define new(T, ...) ({										\
        T##_t* this = (T##_t*) malloc(sizeof(T##_t));			\
        logging(this, #T" created.", "creating "#T" failed.");	\
        T##_constructor(this, ##__VA_ARGS__);					\
        this; })
    #define delete(T) ({ free(T); T = NULL; })
  • overloading with _Generic ?
  • segmentation fault when library linking and solved with VTable

Conclusion

Boom !! Want to learn OOP ? Then you should definitely try C !!