Revision History Revision 0: (19 September 2007) Published. Revision 1: (19 September 2007) Modified the dummy header.
Introduction
A class, in the immortal words of Dr. Neat, is defined by three things: 1) constructor(s), 2) fields, and 3) methods.Since this tutorial is written for Java programmers, I will focus on the implementation of classes in C by using
struct
s, function pointers, etc.
One might ask "Why should I bother learning C since C++ is so much more convenient for me?" Well, that's a valid question a Java programmer may ask. The answer is that C, even object oriented C, is more sparing on the resources of the target machine.
So if you are doing embedded programming, you would want to opt for C rather than C++. If you are doing, e.g., generic programming of a calculator, or a shell, or something, you might want to use C++ instead.
But if one is programming on a machine with one megabyte of RAM and a 300 MHz processor, C is your language of choice.
Objects, Objects, Objects, and Objects
The inspiration of object oriented C comes from the virtual file system implementation on SunOS 2.0 from 1985 (see the immortal technical paper that describes the virtual file system implementation).What we do is use
struct
s as the object, function pointers as the methods, and - because we are using struct
s - the fields are already taken care of.
There are two approaches one can take: one is to follow the orthodox vnode approach and have a struct for the fields and a struct for the operations, or follow the lazy approach and use one struct.
Consider the following object:
1. struct object {
2. int id;
3. void (*dumpState)(struct object *this);
4. bool (*equals)(struct object *this, struct object *o);
5. char* (*toString)(struct object *this);
6. };
Line 2 is the field of the object
class, lines 3-5 are the methods of the class. Note how the first argument of all the methods is the this
pointer.
This is similar to Python's object oriented approach.
The alternate approach would be to do the following:
01. struct object {
02. int id;
03. struct obj_ops *ops;
04. };
05.
06. struct obj_ops {
07. void (*dumpState)(struct object *this);
08. bool (*equals)(struct object *this, struct object *o);
09. char* (*toString)(struct object *this);
10. };
Note how the operations are encapsulated in one struct
and the fields are in the other. This is the orthodox approach.
The problem with this approach is that if one wants to get the object's
toString()
method, the line of code would look like: obj.ops->toString(obj);
as opposed to a simpler obj.toString(obj)
.
The UNIX KISS
This may be impresive to some, but I'm sure someone has said "Hey, if thetoString()
method takes in an struct object
object anyways, why not just use the line of code toString(obj)
?"
That is actually what Unix did. So if we were to take a renewed look at the list of operations and the structure, what we would do is in a hypothetical header write:
/* object.h header file */
struct object {
int id;
};
void dumpState(struct object *this);
bool equals(struct object *this, struct object *o);
char* toString(struct object *this);
/* end of the header file */
Then the "methods" of the class would be implemented by simply calling the function and making the first argument the callee.
1 comment:
So what's happened to your quest to learn some physics?
Myke.
Post a Comment