Skip to content

Instantly share code, notes, and snippets.

@dylan-evans
Created May 23, 2011 03:16
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dylan-evans/986154 to your computer and use it in GitHub Desktop.
Save dylan-evans/986154 to your computer and use it in GitHub Desktop.
Example implementation of prototype programming in C
/*
* 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 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);
}
}
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