Greets,
Since C doesn't support OO directly, we'll have to roll our own scheme.
Keeping things as simple and as C-like as possible is desirable. The
more that Lucy looks like other C apps, the easier it will be for
people to contribute.
Here are two full-featured OO architectures which are very
interesting and from which we might draw some ideas, but which would
be overkill for our purposes:
http://ldeniau.home.cern.ch/ldeniau/html/oopc/oopc.html
http://www.planetpdf.com/developer/article.asp?ContentID=6635
Both Ferret and KinoSearch implement OO via structs which include
methods as function pointers within them. They diverge in how
inheritance is handled, though the differences are somewhat
superficial. Here's an illustration of how they work using "Animal"
as a superclass and "Dog" as a subclass.
typedef struct Animal Animal;
typedef struct Dog Dog;
Ferret creates a base class, then includes the base class struct as
the first member in a subclass struct:
struct Animal {
char *name;
char* (*speak)(Animal *self);
};
struct Dog {
Animal super;
void (*chase_cat)(Dog *self);
};
KinoSearch uses macros to manage inheritance.
#define Animal_Base_Struct \
char *name; \
char* (*speak)(Animal *self);
struct Animal {
Animal_Base_Struct
}
struct Dog {
Animal_Base_Struct
void (*chase_cat)(Dog *self);
}
These schemes differ in how casts get applied, but they're pretty
close to the same thing.
/* Ferret */
#define ANIMAL(x) ((Animal*)x)
static void
chase_cat(Dog *self)
{
ANIMAL(self)->speak(self);
scamper();
}
/* KinoSearch */
static void
chase_cat(Dog *self)
{
self->speak((Animal*)self);
scamper();
}
Obviously, things have been working out well for both KS and Ferret
with this architecture. However, including all methods as function
pointers directly within the primary object has a big drawback: it's
wastes memory, especially if we try to establish a base class Obj
with a bunch of handy methods like to_string(), compare(), clone(),
and so on.
Ferret's InStream class points the way forward, implementing
something akin to a poor man's virtual table. InStream objects
include a struct member instream->m, which is a pointer to either one
of two InStreamMethods structs, depending on whether the InStream is
using a ram file or not. The InStreamMethods struct has four
members: read_i, seek_i, length_i, and close_i. The InStream object
thus gets four methods for the price of one.
The cost of a double-dereference to dispatch a method is
insignificant. However, the syntax is godawful...
len = instream->m->length_i(instream);
... which is why Dave has macro-fied it:
#define is_length(mis) mis->m->length_i(mis)
How about if we implement every method call in Lucy that way?
Each class gets its own virtual table, where all method pointers are
ensconced. Class data, e.g. class name, can also go there. However,
we don't have to go whole hog and implement all the features of C++;
this gets us basic inheritance and polymorphism, which is enough to
get by.
One thing that's a little funky about Dave's is_length macro is that
it looks like a function call. I think it's important to distinguish
visually between function calls and methods, so if we go this route,
I suggest we reserve the capitalization scheme currently reserved for
non-constant macros under the Lucy style guide for method calls instead.
#define Animal_Speak(self) (self)->m->speak((Animal*)self)
static chase_cat(Dog *self)
{
Animal_Speak(self);
scamper();
);
Animal_Speak(self) is easier to parse visually than either of these:
self->speak((Animal*)self);
ANIMAL(self)->speak(self);
So in addition to making it possible to add methods to any class for
free, this scheme also cleans up the code visually where it matters
most: in the meat of the library.
There are costs to this approach. It requires a fair amount of
boilerplate code. (We might want to consider using a symbol
generator.) I'm also pretty sure we'll want to write some
bootstrapping code to prep the virtual tables at startup, because it
will be difficult if not impossible to resolve all aspects of
inheritance at compile-time.
Thoughts?
Marvin Humphrey
Rectangular Research
http://www.rectangular.com/