Phils guide to object-oriented ANSI C

I learned this technique in "Programming 101" in college.

(yes, that was almost literally the name of the course :-)]

I posted this to the Debian developers' mailing list, and thought I'd clean up a copy and put it here as well to share with folks.

I have to preface by saying that this does not LOOK like object-oriented programming, for those people coming from a java or C++ background. But if you think about it, it is.

Here is how an end developer would use a "FooOBJ" type object:

blah.c


#include "FooOBJ.h"
void diddle(){
	FooOBJ fobj;
	fobj=newFooOBJ();
	setFooNumber(fobj, 1);
	setFooString(fobj, "somestring");
	dumpFooState(fobj);
	deleteFooOBJ(fobj);
}

int main(){
	diddle();
	return 0;
}


So there you have it; Complete object opacity. PLUS, the caller will not be able to accidentally pass in a non FooOBJ, because of the typedef.

"What typedef?"

See below :-)

This is BETTER object opacity than C++. Why? Because callers have **NO IDEA** what the private members really are, and how they are implemented.

FooOBJ.h consists of the following:

FooOBJ.h


typedef struct fooobj * FooOBJ;
FooOBJ newFooOBJ();
void setFooNumber(FooOBJ,int);
void setFooString(FooOBJ,char *); /* make comments about copy or not here */
void dumpFooState(FooOBJ);      /* dumps debug contents of FooOBJ to stdout */
void deleteFooOBJ(FooOBJ);


You simply cannot get better data hiding than that.

And just to spell things out and give you something to play with, here's a sample implementation of FooOBJ.o Note that there are quite a few different possibilities for it, and it doesnt matter to the "blah.c" code above. All that it needs is a FooOBJ.o file to link against.

Which is what object oriented programming is all about!

FooOBJ.c


#include <stdio.h>
#include "FooOBJ.h"

struct fooobj {
	int privateint;
	char *privateString;
	/* Depending on your preferences, you
	 * may prefer privateString to be a char buffer[],
	 * OR malloc it and free on delete.
	 */
};

/* Any "class static" vars simply get declared as
static int staticvar;
 * or whatever you like here.
 * Whereas instance variables go in the struct above.
 */

FooOBJ newFooOBJ(){
	FooOBJ foo=(FooOBJ)malloc(sizeof(struct fooobj));
	return foo;
}

void setFooNumber(FooOBJ foo,int num){
	if(foo==NULL) return;  /* you may chose to debugprint something
				*instead
				*/
	foo->privateint=num;
}

void setFooString(FooOBJ foo,char *string){
	/* If you malloced the string, you'll want to free() it here!!*/
	if(foo==NULL) return;
	foo->privateString=string;
}
	

void dumpFooState(FooOBJ foo){
	if(foo==NULL) return;
	printf("value of private int==%d\n", foo->privateint);
	printf("value of private string==%s\n", foo->privateString);
}

void deleteFooOBJ(FooOBJ foo){
	/* dont forget to free privateString if you malloced it here!!*/
	free(foo);
}

Compiling hint

If you are unfamiliar with C compiling, you will probably want to compile the above files in the following manner:
cc -c *.c
cc -o foo *.o
Then if you want to play with the implementation of the "FooOBJ" object, you can change just the implementation part with:
cc -c FooOBJ.c
cc -o foo *.o


phil@bolthole.com bolthole.com main page