Posts Tagged ‘inheritance’

Object-based programming in C

Monday, January 21st, 2008

Embedded developers abandon C++ in droves. According to the 2007 survey published in the ESD magazine, the C++ use declined by one-third compared to year before, which was offset by an equal rise in popularity of C—the only viable alternative in embedded.

Even though the last year was most dramatic, the trend has been actually continuing for a number of years. This couldn’t go unnoticed by UML tool vendors, who desparately have been trying to cater to C programmers. For example, you can check out the DDJ article “UML for C Programmers” (which seems to be pretty exact re-print of the Embedded Systems Conference paper “UML for C-Based Embedded Systems“). To my surprise, nether this article, nor the ESC class mention any well-known techniques of mapping objects and classes to C. I’m sure that it is not what UML vendors like. After all, UML is crippled without objects. (The only real meat remaining are state machines.) But I suppose that the marketing departments of I-Logix/Telelogic have done their homework. Apparently embedded developers don’t like to hear about objects anymore.

I find this really disturbing. It seems that “object” and (pardon my language) “class” are becoming dirty words in the embedded circles. C++ decline is one thing. But abandoning objects is a different story. Aren’t we throwing out the baby with the bath water?

One would assume that the 21st-century software developers have objects in their bones and everyone knows how to program with objects in any language, including C. Apparently increasing number of us don’t know that object technology is a way of design, not the use of any particular language or tool. Most design and implementation techniques now associated with C++, Smalltalk, or Java, actually long predate these languages.

So here is how you implement a Point class in C (a Point that you can put on a screen):

typedef struct PointTag {
    int16_t x; /* x-coordinate */
    int16_t y; /* y-coordinate */
} Point;

void Point_ctor(Point *me, int16_t x, int16_t y) {
    me->x = x;
    me->y = y;
}

void Point_move(Point *me, int16_t dx, int16_t dy) {
    me->x += dx;
    me->y += dy;
}

int16_t Point_dist(Point const *me, Point const *other) {
    int16_t dx = me->x – other->x;
    int16_t dy = me->y – other->y;
    return (int16_t)sqrt(dx*dx + dy*dy);
}
. . .

/* example of using Point objects */
    Point foo, bar, tar; /* multiple instances of Point */
    int16_t dist;
    Point_ctor(&foo, 0, 0);
    Point_ctor(&bar, 1, 1);
    Point_ctor(&tar, -1, 2);
    dist = Point_dist(&foo, &bar);
    Point_move(&tar, 2, 4);
    dist = Point_dist(&bar, &tar);
    . . .

You can create any number of Point objects as instances of the Point struct. You need to initialize each point with the “constructor” Point_ctor(). You manipulate the Points only through the provided functions, which take the pointer “me” as the first argument. The “me” pointer corresponds directly to the implicit “this” pointer in C++.

Moreover, you can as easily implement single inheritance. Assume for example, that you need to add a color attribute to Points. Instead of developing such a colored-Point from scratch, you can inherit most what’s common from Point and add only what’s different. Here’s how you do it:

typedef struct ColoredPointTag {
    Point super; /* derives from Point */
    uint16_t color; /* 16-bit color */
} ColoredPoint;

void ColoredPoint_ctor(ColoredPoint *me,
         int16_t x, int16_t y, uint16_t color)
{
    Point_ctor(&me->super, x, y); /* call superclass’ ctor */
    me->color = color;
}
...

/* example of using ColoredPoint objects */
    ColoredPoint p1, p2;int16_t dist;
    ColoredPoint_ctor(&p1, 0, 2, RED);

    ColoredPoint_ctor(&p2, 0, 2, BLUE);
    /* re-use inherited function */
    dist = Point_dist((Point *)&p1, (Point *)&p2);

As you can see, you implement inheritance by literally embedding the superclass (Point) as the first member of the subclass (ColoredPoint). Such nesting of structures always aligns the first data member ‘super’ at the beginning of every instance of the derived structure. This alignment is guaranteed by the C standard. Specifically, WG14/N1124 Section 6.7.2.1.13 says: “… A pointer to a structure object, suitably converted, points to its initial member. There may be unnamed padding within a structure object, but not at its beginning”. This alignment lets you treat a pointer to the derived ColoredPoint struct as a pointer to the Point base struct. All this is legal, portable, and blessed by the Standard.

With this arrangement, you can always safely pass a pointer to ColoredPoint to any C function that expects a pointer to Point. Consequently, all functions designed for the Point structure are automatically available to the ColoredPoint structure. They are all inherited.

There is really nothing to it.