Created
May 23, 2011 03:16
-
-
Save dylan-evans/986154 to your computer and use it in GitHub Desktop.
Example implementation of prototype programming in C
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* An example of prototype programming in C | |
*/ | |
#include <unistd.h> | |
#include <stdio.h> | |
#include <string.h> | |
#include "proto.h" | |
/* | |
* A prototype capable hello world method. | |
* Note that arguments are passed as a Prototype object which is aimed at | |
* keeping C code simpler and easily allowing variable and optional arguments. | |
*/ | |
void * hello(Prototype *self, Prototype *args) | |
{ | |
char *name; | |
Slot *s; | |
s = _get_slot(self, "name"); | |
if(s && (strlen(s->value->value.s) > 0)) { | |
// This is admittedly a bit wierd | |
name = s->value->value.s; | |
} else { | |
name = "World"; | |
} | |
printf("Hello, %s!\n", name); | |
} | |
int main() | |
{ | |
Prototype *obj, *bob, *args; | |
Slot *s; | |
char *val = "bar"; | |
// Create an args object (as an empty arg list) | |
args = proto_new(NULL); | |
// Create a new object | |
obj = proto_new(NULL); | |
// Assign obj.foo = "bar" | |
_assign_value(obj, "foo", TYPE_STRING, val); | |
// Retrieve obj.foo | |
s = _get_slot(obj, "foo"); | |
// Print the result | |
if(s) { | |
printf("** obj.foo = '%s'\n", s->value->value.s); | |
} | |
// Define the hello as obj.hi | |
proto_define(obj, "hi", hello); | |
// Create derived object bob from obj | |
bob = proto_new(obj); | |
// Assign bob.name = "Bob" | |
_assign_value(bob, "name", TYPE_STRING, "Bob"); | |
// Send the message, like bob.hi() | |
printf("** Sending bob.hi message:\n"); | |
proto_send(bob, "hi", args); | |
printf("** Sending obj.hi message:\n"); | |
proto_send(obj, "hi", args); | |
printf("\n** Dumping data for bob\n"); | |
_dump_proto(bob); | |
return 0; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* This is an example of prototype programming implemented in C. | |
* On first glace it may seem difficult to use prototypes in C however the | |
* language is naturally classless so it lacks the heavily structured data model | |
* of an object oriented language. | |
* | |
* This example is a very basic prototype system the use of which would | |
* resemble an extension for an interpreted language more than an actual C | |
* program. This approach attempts addresses several problems which occur | |
* when using OOP in a C environment. | |
* | |
* - Prototypes are implicitly dynamic in nature, whereas the C environment | |
* is strictly static. | |
* | |
* - In C there is no simple way to pass a method the object to which | |
* it belongs. | |
* | |
* This is not a finished product, just an example implementation, use at your | |
* own peril. | |
*/ | |
#include <stdio.h> | |
#include <unistd.h> | |
#include <malloc.h> | |
#include <string.h> | |
#include "proto.h" | |
/* | |
* The high level interface is incomplete lacking a concrete model for type | |
* translation and access. So in practice the low-level interface must be used. | |
* | |
* The low level interface is broadly defined as any function which accesses | |
* Slot and Type, ideally the high level interface would deal purely with C | |
* types. | |
*/ | |
/**** High Level Interface ****/ | |
/* | |
* Create a new object from base. | |
* If base is NULL then the object will be empty. | |
*/ | |
Prototype *proto_new(Prototype *base) | |
{ | |
Prototype *p; | |
p = malloc(sizeof(Prototype)); | |
p->parent = base; | |
p->first = p->last = NULL; | |
return p; | |
} | |
/* | |
* Define a function as a slot | |
*/ | |
void *proto_define(Prototype *p, char *name, void *(*function)(Prototype *, Prototype *)) | |
{ | |
Type *type; | |
type = malloc(sizeof(Type)); | |
type->ref = TYPE_FUNCTION; | |
type->value.function = function; | |
_create_slot(p, name, type); | |
} | |
/* | |
* Send a message to a prototype object. | |
* The args parameter is an object which contains the arguments for | |
* the named slot. This further abstracts C to allow a _simplified_ method | |
* for variable argument methods. | |
*/ | |
void *proto_send(Prototype *p, char *name, Prototype *args) | |
{ | |
Slot *s; | |
s = _get_slot(p, name); | |
if(!s || !(s->value->ref == TYPE_FUNCTION)) { | |
return NULL; | |
} | |
return s->value->value.function(p, args); | |
} | |
/**** Low Level Interface ****/ | |
/* | |
* Assign a value to a slot with the given type code | |
*/ | |
void _assign_value(Prototype *p, char *name, int code, void *value) | |
{ | |
Type *type; | |
type = malloc(sizeof(Type)); | |
type->ref = code; | |
type->value.misc = value; | |
_create_slot(p, name, type); | |
} | |
/* | |
* Create a slot on a prototype | |
* Slots are created in an alphabetical order (although no attempt is made to | |
* optimize access at this stage) | |
*/ | |
void _create_slot(Prototype *p, char *name, Type *value) | |
{ | |
Slot *s, *cur; | |
int comp; | |
s = malloc(sizeof(Slot)); | |
s->name = name; | |
s->value = value; | |
if(!p->first) { | |
// This is the first slot | |
s->next = s->prev = NULL; | |
p->first = p->last = s; | |
return; | |
} | |
for(cur = p->first; cur; cur = cur->next) { | |
comp = strcmp(s->name, cur->name); | |
if( comp == 0 ) { | |
// Matching slot, so replace it. | |
s->next = cur->next; | |
s->prev = cur->prev; | |
cur->prev->next = s; | |
cur->next->prev = s; | |
cur->prev = cur->next = NULL; | |
_destroy_slot(cur); | |
return; | |
} else if(comp < 0) { | |
// Insert s before the cur - s->name is earlier in the alphabet | |
// than cur->name | |
if(p->first == cur) { | |
p->first = s; | |
} | |
s->prev = cur->prev; | |
cur->prev = s; | |
s->next = cur; | |
if(s->prev) { | |
s->prev->next = s; | |
} | |
return; | |
} | |
} | |
// Append the slot | |
s->prev = p->last; | |
s->next = NULL; | |
p->last->next = s; | |
p->last = s; | |
} | |
/* | |
* Retrieve a slot using the parent delegate to recursively search for slots | |
*/ | |
Slot *_get_slot(Prototype *p, char *name) | |
{ | |
Slot *cur; | |
for(cur = p->first; cur; cur = cur->next) { | |
if(strcmp(cur->name, name) == 0) { | |
return cur; | |
} | |
} | |
// Delegate to the parent | |
if(p->parent) { | |
return _get_slot(p->parent, name); | |
} | |
return NULL; | |
} | |
/* | |
* Remove a slot from any linked list and free it. | |
* Note that this function does not update the prototype. | |
*/ | |
void _destroy_slot(Slot *s) | |
{ | |
// Remove slot from the linked list if any | |
if(s->next) { | |
s->next->prev = s->prev; | |
} | |
if(s->prev) { | |
s->prev->next = s->next; | |
} | |
free(s); | |
} | |
/* | |
* Print details of an objects slots | |
*/ | |
void _dump_proto(Prototype *p) | |
{ | |
Slot *cur; | |
printf("Object:\n"); | |
for(cur = p->first; cur; cur = cur->next) { | |
if(cur->value && cur->value->ref == TYPE_STRING) { | |
printf("Slot: %s = '%s'\n", cur->name, cur->value->value.s); | |
} else { | |
printf("Slot: %s\n", cur->name); | |
} | |
} | |
if(p->parent) { | |
printf("Parent > "); | |
_dump_proto(p->parent); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
typedef struct _prototype Prototype; | |
typedef struct _slot Slot; | |
typedef struct _type Type; | |
/* | |
* Prototype: | |
* A prototype is an object which contains a list of slots which may refer to | |
* either an associated function or a simple data type | |
*/ | |
struct _prototype { | |
Prototype *parent; | |
Slot *first, *last; | |
}; | |
struct _slot { | |
Slot *prev, *next; | |
char *name; | |
Type *value; | |
}; | |
#define TYPE_FUNCTION 1 | |
#define TYPE_INT 2 | |
#define TYPE_FLOAT 3 | |
#define TYPE_STRING 4 | |
struct _type { | |
int ref; | |
union { | |
void * (*function)(Prototype *, Prototype *); | |
int i; | |
float f; | |
char *s; | |
void *misc; | |
} value; | |
}; | |
/* High level interface */ | |
Prototype * proto_new(Prototype *base); | |
void * proto_call(Prototype *, char *, void *); | |
void * proto_send(Prototype *, char *, Prototype *); | |
void * proto_define(Prototype *, char *, void *(*)(Prototype *, Prototype *)); | |
/* Low level interface */ | |
void _create_slot(Prototype *, char *, Type *); | |
void _destroy_slot(Slot *); | |
Slot *_get_slot(Prototype *, char *); | |
void _assign_value(Prototype *, char *, int, void *); | |
void _dump_proto(Prototype *); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment