分类: C/C++
2006-06-23 13:13:39
//////////////////////////////////////////////////////////////////////////
// 02. OOP in C //
// By: Brian Spilsbury //
// AKA Zhivago @EFnet's #c //
// Email: brian@bizo.biz.usyd.edu.au //
//////////////////////////////////////////////////////////////////////////
It is possible to write object oriented programs in plain C. This is often
uglier, and more dangerous than doing it in an object oriented langauge but
you can do it.
There are several methods which can be used.
Firstly if we are dealing with a prototype style oop then we don't need
to worry about classes, and we can use C's file-level encapsulation to
restrict the interface.
For example, if we have an accessor object whose interface is the methods
Open, Close, Read and Write, but has the internal methods Foo,
Zog, and Harry, then we could implement it like this
--- accessor.h ---
int Open(char *name):
int Close(int handle):
int Read(int handle, char *buf, int len):
int Write(int handle, char *buf, int len):
--- mymodule.c --- { ... } is elided code
int Open(int handle, char *name) { ... }
int Close(int handle) { ... }
int Read(int handle, char *buf, int len) { ... }
int Write(int handle, char *buf, int len) { ... }
static int Foo() { ... }
static int Zog(int x) { ... }
static int Harry(char *x, int l) { ... }
---
So we use the static keyword to prevent external access to a symbol.
This may not appear terribly useful at first, but consider where you
are writing a module to be dynamically loaded into a server, this is case
where the module needs to conform to an Interface, and is essentially an object.
Multiple instances may be referenced by passing a handle to the interface,
and the allocation/deallocation can be handled internally to the class.
A more usual form of OOP in C can be achieved by using structures and function
pointers. We can even implement a form of inheritance by overlapping
structures.
--- widget.c
int drawWidget(struct widget *w); /* not virtual */
typedef struct widget {
char *name;
int x, y;
} widget;
typedef struct callbackWidget {
struct widget tag;
int (*callback)(struct callbackWidget *w); /* virtual */
int cbValue;
} callbackWidget;
typedef struct pressWidget {
struct callbackWidget tag;
int (*press)(struct pressWidget *w, int x, int y); /* virtual */
} pressWidget;
int drawWidget(struct widget *w)
{
printf("I drew da widget.\n");
}
void foo(pressWidget *x)
{
drawWidget((widget *)x);
x->press(x, 0, 0);
}
int pressWidgetPress(pressWidget *w, int x, int y)
{
printf("Hey, I got pressed.\n");
}
main()
{
pressWidget a;
a.press = pressWidgetPress;
foo(&a);
}
drawWidget() is a non virtual method, and press is a virtual method.
With this method we're pretty much limited to what C++ can do, but we
don't get the typing benefits, or the syntactical sugar.
There is one last form that I'm going to touch on today, and that is the use
of a function as an encapsulating form, with objects referenced by opaque
handles.
We can define a function as being the interface to an object, so essentially
it is an class. Unfortuantely this limits us to a single actual interface
so we'll probably need to use tuples to effectively communicate.
void Button(int handle, tuple *in, tuple **out)
{
static button *buttons[10]; /* dodgey 10 buttons max implementation */
if (buttons[handle] == NULL || handle < 0 || handle > 9) {
*out = NULL;
return;
}
switch(in[0]) {
case NEW: ...
break;
case DRAW: ...
break;
}
return;
}
Here the tuples are probably just int arrays.
I haven't actually used this last method, and it appears to be rather clumsy
and prone to needing too much typing, but it does give solid encapsulation.
Inheritance can be implemented by chaining 'bits' of objects as objects, and
then calling superclasses with the handle to the super-object and the
appropriate tuples.
Ie, I have a pictureButton() class, this inherits the button() class.
When I create an instance of the pictureButton class, it creates an instance
of the button class, stores the handle in the instance of the pictureButton
class that it creates, and then returns the handle to the pictureButton
class.
Then when I ask the pictureButton to draw, it calls button with the button
handle it has, with draw to draw the frame, etc, and then draws the picture
inside the frame itself, using the information in its pictureButton interface.
I don't realistically recommend this last method for any solution.
Having said this, OOP in C is often undesirable, often it is better served
by structured code and a modular design, but the concepts that OOP pushes
do fit quite well into modular/structured design, encapsulation, polymorphism
being the most usable features, with inheritance being somewhat less usable
(except where it fits in with polymorphism).
If you want to do OOP in C for itself I would recommend Objective C, or C++.
If you want to do C, but use some OOP principles, then these techniques may
be of some interest.