views:

258

answers:

7

I read that early C++ "compilers" actually translated the C++ code to C and used a C compiler on the backend, and that made me wonder. I've got enough technical knowledge to wrap my head around most of how that would work, but I can't figure out how to do class inheritance without having language support for it.

Specifically, how do you define a class with a few fields, then a bunch of subclasses that inherit from it and each add their own new fields, and be able to pass them around interchangeably as function arguments? And especially how can you do it when C++ allows you to allocate objects on the stack, so you might not even have pointers to hide behind?

NOTE: The first couple answers I got were about polymorphism. I know all about polymorphism and virtual methods. I've even given a conference presentation once about the low-level details of how the virtual method table in Delphi works. What I'm wondering about is class inheritance and fields, not polymorphism.

+5  A: 

In C anyway you an do it the way cfront used to do it in the early days of C++ when the C++ code was translated into C. But you need to be quite disciplined and do all the grunt work manually.

Your 'classes' have to be initialized using a function that performs the constructor's work. this will include initializing a pointer to a table of polymorphic function pointers for the virtual functions. Virtual function calls have to be made through the vtbl function pointer (which will point to a structure of function pointers - one for each virtual function).

The virtual function structure for each derived calss needs to be a super-set of the one for the base class.

Some of the mechanics of this might be hidden/aided using macros.

Miro Samek's first edition of "Practical Statecharts in C/C++" has an Appendix A - "C+ - Object Oriented Programming in C" that has such macros. It looks like this was dropped from the second edition. Probably because it's more trouble than it's worth. Just use C++ if you want to do this...

You should also read Lippman's "Inside the C++ Object Model" which goes into gory details about how C++ works behind the scenes, often with snippets of how things might work in C.


I think I see what you're after. Maybe.

How can something like this work:

typedef 
struct foo {
    int a;
} foo;

void doSomething( foo f);   // note: f is passed by value

typedef
struct bar {
    foo base;
    int b;
} bar;


int main() {
    bar b = { { 1 }, 2};

    doSomething( b);    // how can the compiler know to 'slice' b
                        //  down to a foo?

    return 0;
}

Well you can't do that as simply as that without language support - you'd need to do some things manually (that's what it means to not have language support):

    doSomething( b.base);       // this works
Michael Burr
Good answer, but it's not what I was asking about.
Mason Wheeler
Re: the edit: OK, that makes a bit more sense.
Mason Wheeler
+5  A: 

Basically, structs-within-structs.

struct Base {
    int blah;
};

struct Derived {
    struct Base __base;
    int foo;
};

When you want to, say, cast a Derived * to Base *, you'd actually return a pointer to the __base element of the Derived struct, which in this case is the first thing in the struct so the pointers should be the same (wouldn't be the case for multiple-inherited classes though).

If you want to access blah in this case, you would do something like derived.__base.blah.

Virtual functions are normally done with a special table of function pointers that is part of each object, a rudimentary sort of "what is my type" record.

Artelius
Aha! That makes sense. And it explains a few of C++'s uglier corners when dealing with the inheritance heirarchy too.
Mason Wheeler
+2  A: 

Here is how COM does it for C language. I am a bit rusty at this , but the essence works like this. Each "class" member variables is just a struct.

struct Shape
{
  int value;
};

struct Square
{
  struct Shape shape; // make sure this is on top, if not KABOOM
  int someothervalue;
};

all the methods, are actually just normal functions. like this

void Draw(Shape * shape,int x,int y)
{
  shape->value=10; // this should work even if you put in a square. i think...
}

then, they use the preprocessor to "trick" the C code into displaying something like this.

Square * square;
square->Draw(0,0); // this doesnt make sense, the preprocessor changes it to Draw(square,0,0);

Alas, i dont know what kind of preprocessor tricks are done to make the C++ looking function call resolve into a plan vanilla C call.

DirectX COM objects are declared this way.

Andrew Keith
A: 

If you want a good reference on how this stuff works take a look at the glib/gdk/gtk open source libraries. They have pretty good documentation and the entire framework is based on C OO.

ssteidl
+1  A: 

Dr. Dobb's had a moderately detailed article on this topic, Single Inheritance Classes in C.

Boojum
A: 

You can simulate an object by writing constructors, setters, getters, and destructors with the hidden this pointer called out explicitly.

Inheritance is handled by having the derived object include a pointer to the base object in the structure of the derived object.

David Harris
A: 

Structs-within-structs is common, but it makes it a pain to access inherited fields. You either need to use indirection (e.g. child->parent.field), or casting (((PARENT *) child)->field).

An alternative I have seen is more like this:

#define COUNTRY_FIELDS \
    char *name; \
    int population;

typedef struct COUNTRY
{
    COUNTRY_FIELDS
} COUNTRY;

#define PRINCIPALITY_FIELDS \
    COUNTRY_FIELDS \
    char *prince;

typedef struct PRINCIPALITY
{
    PRINCIPALITY_FIELDS
} PRINCIPALITY;

This gives types with direct access to inherited fields. The resulting objects can still be safely cast to the parent type, because the parent's fields and the inherited fields start at the same place.

The syntax can be improved a little with macros. I saw this in the older POV-Ray source (but I think they've since converted to C++).

Edmund