Sunday 2 February 2014

Can you write object oriented code in C?

Since you're talking about polymorphism then yes, you can, we were doing that sort of stuff years before C++ came about.
Basically you use a struct to hold both the data and a list of function pointers to point to the relevant functions for that data.
So, in a communications class, you would have an open, read, write and close call which would be maintained as four function pointers in the structure, alongside the data for an object, something like:
typedef struct {
int (*open)(void *self, char *fspec);
int (*close)(void *self);
int (*read)(void *self, void *buff, size_t max_sz, size_t *p_act_sz);
int (*write)(void *self, void *buff, size_t max_sz, size_t *p_act_sz);
// And data goes here.
} tCommClass;

tCommClass commRs232
;
commRs232
.open = &rs232Open;
: :
commRs232
.write = &rs232Write;

tCommClass commTcp
;
commTcp
.open = &tcpOpen;
: :
commTcp
.write = &tcpWrite;
Of course, those code segments above would actually be in a "constructor" such as rs232Init().
When you 'inherit' from that class, you just change the pointers to point to your own functions. Everyone that called those functions would do it through the function pointers, giving you your polymorphism:
int stat = (commTcp.open)(commTcp, "bigiron.box.com:5000");
Sort of like a manual vtable.
You could even have virtual classes by setting the pointers to NULL -the behaviour would be slightly different to C++ (a core dump at run-time rather than an error at compile time).
Here's a piece of sample code that demonstrates it. First the top-level class structure:
#include <stdio.h>

// The top-level class.

typedef struct _tCommClass {
int (*open)(struct _tCommClass *self, char *fspec);
} tCommClass;
Then we have the functions for the TCP 'subclass':
// Function for the TCP 'class'.

static int tcpOpen (tCommClass *tcp, char *fspec) {
printf
("Opening TCP: %s\n", fspec);
return 0;
}
static int tcpInit (tCommClass *tcp) {
tcp
->open = &tcpOpen;
return 0;
}
And the HTTP one as well:
// Function for the HTTP 'class'.

static int httpOpen (tCommClass *http, char *fspec) {
printf
("Opening HTTP: %s\n", fspec);
return 0;
}
static int httpInit (tCommClass *http) {
http
->open = &httpOpen;
return 0;
}
And finally a test program to show it in action:
// Test program.

int main (void) {
int status;
tCommClass commTcp
, commHttp;

// Same 'base' class but initialised to different sub-classes.

tcpInit
(&commTcp);
httpInit
(&commHttp);

// Called in exactly the same manner.

status
= (commTcp.open)(&commTcp, "bigiron.box.com:5000");
status
= (commHttp.open)(&commHttp, "http://www.microsoft.com");

return 0;
}
This produces the output:
Opening TCP: bigiron.box.com:5000
Opening HTTP: http://www.microsoft.com

0 comments:

Post a Comment

Twitter Delicious Facebook Digg Stumbleupon Favorites More