Multiple Inheritance
I've been working on an ongoing pet project. It's had many faces thus far, but
its current one is in C and was started at the beginning of summer. Including
test cases and currently broken stuff, the line count is 2176. But what does
the code do? Closures, data buffer, doubly linked list, and binary search
tree. Sometime soon I hope to get red-black trees working as well. Enough
about how little code was actually produced... as this is the fate of pet
projects.
The interesting bit: Let's examine the is-a/has-a relationship. Certainly
there is a difference, but is it so bold to represent an is-a relationship as
a has-a one in data? Consider the following structures - a binary search tree
node and a red-black tree node.
struct bstnode
{
struct bstnode* p;
struct bstnode* child[2];
};
struct rbtnode
{
struct bstnode bstnode;
bool redp;
};
Indeed, if you wanted to use a rbtnode as a bstnode, you could just cast it
and vice-versa.
struct bstnode* n = (struct bstnode*)
malloc (sizeof (struct rbtnode));
((struct rbtnode*) n)->redp = 1;
But that too got me thinking, I have this buffer:
struct closure
{
void (* fn) (struct closure*);
};
struct buffer
{
struct closure read_source;
size_t eof;
struct dlist bufitr_list;
struct chunkq chunkq;
};
Now, I can argue that a buffer inherits from a closure and a chunkq in this
context. When the buffer runs out of data and needs more, the closure is
called to update it. It "inherits" from a closure because it acts as if the
rest of its data members are local variables to a closure. In this next
example, buffer->read_source.fn = &reader_fn
struct stream_buffer
{
struct buffer buffer;
FILE* stream;
};
void
reader_fn
(struct closure* closure)
{
struct buffer* buf = (struct buffer*) closure;
buf->eof = fread (fillable_buffer (buf),
1,
buf->chunkq.chunk_size,
((struct stream_buffer*) buf)->stream);
}
So you can see that the closure is casted to a buffer as if the buffer
inherits from it. Now, inheritance in this case is a bit of a stretch, but you
can see the parallels. A buffer inheriting from a chunkq is more obvious, I
was originally going to make the buffer do everything the chunkq does, but I
decided it would be best to modularize the chunkq-specific functions a bit.
What is a chunkq? A queue of data chunks. Each chunk is the same size. Easy
enough?
Now to the point. It would be beneficial to be able to treat a buffer as both
a closure and a chunkq. At present, it only shares identity with a closure out
of necessity. I was pondering today the prospect of multiple inheritance using
this model. For one, overloading/overriding functions need not be considered
since the inheritance is purely in data. So, all that one needs to do such
inheritance is a way to offset the pointer to point to the buffer's data
member which is inherited from when casting to it and vice-versa when casting
back from it.
Since the offset would be data member specific and not data-type specific,
such a cast would look like...
struct buffer* buf = some_crap;
struct chunkq* q = (struct buffer -> chunkq) buf;
assert (buf == (struct buffer <- chunkq) q);
The only problem here is an extra step processing the code. At this point it's
bordering on language extension! I've though about it, and doing this
completely in the C language is ugly at best. (of course the extra processing
step could be a C program)
I'll just leave it at that. Thanks for reading!
-- Alex
2009.08.06 - last edit >> Thu, 06 Aug 2009 22:21:59 -0400