OOP with C

Now that you've seen how to make C object like, why don't we take it a smidgen further? Can it be made to do some kind of simple inheritance? I thought so. So, I wrote a little demo program to see if I could come up with something.

The first thing I did was make a private variables structure that will be put into the base class.

typedef struct ___pvt {
    int a;
    char b[10];
} __pvt;

The reason it's a struct is so I can get the size of it, and use that to pad the derived classes*. That's done with a macro. This must be the first thing after the struct declaration.

#define BASE_OFFSET           \
char __offset[sizeof(__pvt)];

Next, I'll need some public variables. This doesn't need a struct. It's going to be put in every class right after the private portion. (And when I say class, really I mean struct.)

#define PUB_VAR                  \
char c;                          \
void (*foo);

That's all you need. Below is the base class with private variables a and b and public variables c and foo. Beneath that are two derived classes. They include the offset (which is where the private variables are), followed by the public variables, and last is their extension to the base class. In this case I've defined an integer in one class and a short in the other.

struct base {
    __pvt __pvt_var;
    PUB_VAR;
};

struct derived1 {
    BASE_OFFSET;
    PUB_VAR;
    int a;
};

struct derived2 {
    BASE_OFFSET;
    PUB_VAR;
    short a;
};

There are three pointers to functions defined. So I'm going to define three functions to point to.

void foo_d1(void* d1, char *s, int i)
{
    printf("Came from %s - %d\n",s,i);
    printf("Member a is %d\n",((struct derived1 *)d1)->a);
}

void foo_d2(void* d2, char *s, void *s2)
{
    printf("Came from %s - %s\n",s,(char *)s2);
    printf("Member a is %d\n",((struct derived2 *)d2)->a);
}

void foo(void* b, char *s)
{
    printf("Came from %s\n",s);
    printf("Member a is %d\n",((struct base *)b)->__pvt_var.a);
}

I can't assign these from within the classes. It will have to be done at initialization. That's the first thing that happens in main. I set foo of d1 (derived1) to point to foo_d1, and likewise for d2 and b (base).

struct base b, *b1, *b2;
struct derived1 d1;
struct derived2 d2;

d1.foo = &foo_d1;
d2.foo = &foo_d2;
b.foo = &foo;

Ok, make some assignments and print the results just to make sure everything is accessible in the manner I think it is.

b.__pvt_var.a = 1;
strcpy(b.__pvt_var.b,"this is b");
b.c = 'b';

d1.c = '1';
d1.a = 2;

d2.c = '2';
d2.a = 5;

printf("b.__pvt.a==%d\nb.__pvt.b==%s\nb.c==%c\n",b.__pvt_var.a,b.__pvt_var.b,b.c);
printf("d1.c==%c\nd1.a==%d\n",d1.c,d1.a);
printf("d2.c==%c\nd2.a==%d\n",d2.c,d2.a);
And the results are

b.__pvt.a==1
b.__pvt.b==this is b
b.c==b
d1.c==1
d1.a==2
d2.c==2
d2.a==5

So it looks like b has access to its private parts. The derived classes are somewhat unaware of their private members. Although, it wouldn't be too much trouble to go ahead and access them anyway. Next up is the fun part. I declare and point a base class at the derived classes used above. I can now (easily) assign the private members of the derived classes using the base class pointer. Normally, you woudn't be able to access a private variable outside its class. You could solve this problem by moving the structs into their own files and leaving the definition of the structs out of the header file.

b1 = (struct base *)&d1;
b2 = (struct base *)&d2;
    
b1->__pvt_var.a = 1;
strcpy(b1->__pvt_var.b,"this is b");
printf("b1->__pvt.a==%d\nb1->__pvt.b==%s\nb1->c==%c\n",b1->__pvt_var.a,b1->__pvt_var.b,b1->c);
And the output is

b1->__pvt.a==1
b1->__pvt.b==this is b
b1->c==1

First invoke each of the derived classes functions. And notice that I pass the function a pointer to the struct that invoked it. This would be "self". I should get the same results by using the base class pointers. Look mom -- polymorphism! You can now pass the base class pointers around without other code knowing or caring about the real type. But when you invoke foo, you'll get the derived classes' behaviour. And last I invoke base class's foo with the expected output. There are many obvious problems with this. For example, the sub classes can't call the super method of the base class (because it doesn't exist). But I think what I've done right here gives you some power. And because it's all implemented in the code, it's quite transparent and easy to grok in fullness. Ask yourself this -- how well do you understand what your C++ compiler is doing to your code?

d1.foo(&d1,"d1",10);
d2.foo(&d2,"d2","auxiliary string");
b1->foo(b1,"b1",10); //this is a base class pointer to derived1
b2->foo(b2,"b2","auxiliary string"); //this is a base class pointer to derived2
b.foo(&b,"b");    //this really is base
And the output

Came from d1 - 10
Member a is 2
Came from d2 - auxiliary string
Member a is 5
Came from b1 - 10
Member a is 2
Came from b2 - auxiliary string
Member a is 5
Came from b
Member a is 1

* I have been told this may not always work. The compiler may pad after the inner struct. However, if you had the definition of struct __pvt, you could just include it in the base structures.

prev


home