Why are you actually putting interface function-pointers within the struct
itself? IMVHO, this can be a huge waste of space. A MUCH more space
efficient approach is to include a single pointer to a constant vtable.
Something like this, modulo bugs because I am quickly typing it directly in
the newsreader:
/* common object private vtable */
struct object_prv_vtable {
int (*fp_destroy) (void* const);
};
/* common object interface */
#define object_destroy(t) ( \
(t)->vtable->object.fp_destroy((t)) \
)
/* common device private vtable */
#include <stddef.h>
struct device_prv_vtable {
int (*fp_read) (void* const, void*, size_t);
int (*fp_write) (void* const, void const*, size_t);
};
/* common device vtable */
struct device_vtable {
struct object_prv_vtable const object;
struct device_prv_vtable const device;
};
/* common device object */
struct device {
struct device_vtable const* vtable;
};
/* common device interface */
#define device_read(t, b, s) ( \
(t)->vtable->device.fp_read((t), (b), (s)) \
)
#define device_write(t, b, s) ( \
(t)->vtable->device.fp_write((t), (b), (s)) \
)
There. That's all the scaffolding we need. Now its time to show a concrete
implementation of a device. Lets call the device a `usb_drive'. Here is the
header file:
/* usb_drive.h */
#if ! defined (USB_DRIVE_H)
#define USB_DRIVE_H
extern int usb_drive_create(struct device**);
#endif
Okay, here is the implementation file:
/* usb_drive.c */
#include "usb_drive.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
struct usb_drive {
struct device device;
/* whatever */
};
static int object_destroy(void*);
static int device_read(void*, void*, size_t);
static int device_write(void*, void const*, size_t);
static struct device_vtable const g_vtable[
{ /* object interface */
object_destroy
},
{ /* device interface */
device_read,
device_write
}
];
int usb_drive_create(
struct device** pself
) {
struct usb_drive* const self = malloc(sizeof(*self));
if (self) {
*pself = &self->device;
return 0;
}
return ENOMEM;
}
int object_destroy(void* const self_) {
struct usb_drive* const self = self_;
free(self);
printf("destroyed a usb_drive(%p)\n", self_);
return 0;
}
int device_read(void* const self_, void* buf, size_t size) {
struct usb_drive* const self = self_;
printf("read from a usb_drive(%p, %p, %lu)\n",
self_, buf, (unsigned long) size);
return 0;
}
int device_read(void* const self_, void* buf, size_t size) {
struct usb_drive* const self = self_;
printf("wrote to a usb_drive(%p, %p, %lu)\n",
self_, buf, (unsigned long) size);
return 0;
}
Well, now its time to actually use it:
#include <usb_drive.h>
void read_write(
struct device* const self
) {
char buf[100];
device_read(a_device, buf, 50);
device_write(a_device, buf, 5);
}
int main(void) {
struct device* a_device;
if (! usb_drive_create(&a_device)) {
read_write(a_device);
object_destroy(a_device);
}
return 0;
}
There, that is an example of a minimalist abstract interface technique in C.
Any thoughts?
I seem to remember an old header file that was in some, UNIX I think?,
distributions that implemented the same thing. I cannot remember the name of
the header. Perhaps somebody can refresh my memory.
Ummmm, well, I should go ahead and point the device object to the fuc%ing
VTABLE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! OUCH!!!!
self->device.vtable = &g_vtable;
> return 0;
> }
> return ENOMEM;
> }
>
>
> int object_destroy(void* const self_) {
> struct usb_drive* const self = self_;
> free(self);
> printf("destroyed a usb_drive(%p)\n", self_);
> return 0;
> }
>
>
> int device_read(void* const self_, void* buf, size_t size) {
> struct usb_drive* const self = self_;
> printf("read from a usb_drive(%p, %p, %lu)\n",
> self_, buf, (unsigned long) size);
> return 0;
> }
>
>
> int device_read(void* const self_, void* buf, size_t size) {
> struct usb_drive* const self = self_;
> printf("wrote to a usb_drive(%p, %p, %lu)\n",
> self_, buf, (unsigned long) size);
> return 0;
> }
> [...]
Here ya go:
http://clc.pastebin.com/f52a443b1
fully compliable code.
C++ used a scheme like this before it had templates:
http://h30097.www3.hp.com/cplus/6026pro_genr.html
BTW, please use the message subject to summarize the body, not as the
first few words of the message body; the body should stand on its own.
Bingo! `generic.h' was the EXACT header I was thinking of!
;^)
Thank you blargg; I was on the tip of my mind!
Ouch...
I think the approach A (templates via macros) is pretty good. I wonder
if there is already a ROBUST and consistent library of these, similar
to STL?