Object-orientation in C

前端 未结 22 1771
孤城傲影
孤城傲影 2020-11-22 15:26

What would be a set of nifty preprocessor hacks (ANSI C89/ISO C90 compatible) which enable some kind of ugly (but usable) object-orientation in C?

I am familiar with

22条回答
  •  青春惊慌失措
    2020-11-22 16:07

    I'm a bit late to the party here but I like to avoid both macro extremes - too many or too much obfuscates code, but a couple obvious macros can make the OOP code easier to develop and read:

    /*
     * OOP in C
     *
     * gcc -o oop oop.c
     */
    
    #include 
    #include 
    #include 
    
    struct obj2d {
        float x;                            // object center x
        float y;                            // object center y
        float (* area)(void *);
    };
    
    #define X(obj)          (obj)->b1.x
    #define Y(obj)          (obj)->b1.y
    #define AREA(obj)       (obj)->b1.area(obj)
    
    void *
    _new_obj2d(int size, void * areafn)
    {
        struct obj2d * x = calloc(1, size);
        x->area = areafn;
        // obj2d constructor code ...
        return x;
    }
    
    // --------------------------------------------------------
    
    struct rectangle {
        struct obj2d b1;        // base class
        float width;
        float height;
        float rotation;
    };
    
    #define WIDTH(obj)      (obj)->width
    #define HEIGHT(obj)     (obj)->height
    
    float rectangle_area(struct rectangle * self)
    {
        return self->width * self->height;
    }
    
    #define NEW_rectangle()  _new_obj2d(sizeof(struct rectangle), rectangle_area)
    
    // --------------------------------------------------------
    
    struct triangle {
        struct obj2d b1;
        // deliberately unfinished to test error messages
    };
    
    #define NEW_triangle()  _new_obj2d(sizeof(struct triangle), triangle_area)
    
    // --------------------------------------------------------
    
    struct circle {
        struct obj2d b1;
        float radius;
    };
    
    #define RADIUS(obj)     (obj)->radius
    
    float circle_area(struct circle * self)
    {
        return M_PI * self->radius * self->radius;
    }
    
    #define NEW_circle()     _new_obj2d(sizeof(struct circle), circle_area)
    
    // --------------------------------------------------------
    
    #define NEW(objname)            (struct objname *) NEW_##objname()
    
    
    int
    main(int ac, char * av[])
    {
        struct rectangle * obj1 = NEW(rectangle);
        struct circle    * obj2 = NEW(circle);
    
        X(obj1) = 1;
        Y(obj1) = 1;
    
        // your decision as to which of these is clearer, but note above that
        // macros also hide the fact that a member is in the base class
    
        WIDTH(obj1)  = 2;
        obj1->height = 3;
    
        printf("obj1 position (%f,%f) area %f\n", X(obj1), Y(obj1), AREA(obj1));
    
        X(obj2) = 10;
        Y(obj2) = 10;
        RADIUS(obj2) = 1.5;
        printf("obj2 position (%f,%f) area %f\n", X(obj2), Y(obj2), AREA(obj2));
    
        // WIDTH(obj2)  = 2;                                // error: struct circle has no member named width
        // struct triangle  * obj3 = NEW(triangle);         // error: triangle_area undefined
    }
    

    I think this has a good balance, and the errors it generates (at least with default gcc 6.3 options) for some of the more likely mistakes are helpful instead of confusing. The whole point is to improve programmer productivity no?

提交回复
热议问题