Pros
- Objects can be easily shared between functions and programs without casting
- Very dynamic
- Objects can have multiple inheritances
- Not much awkward casting
- Works across multiple languages and platforms
- Very easy to implement garbage collection
- Easy to serialize objects and import them into any object
Cons
- A little speed loss, but not much
- Objects must be functionally accessed
- Fields must constantly be casted (At least... primitive fields must)
- Requires much more memory
This is my favorite object system thanks to all the pros. It's great for plugin systems.
Now all your functions for accessing objects should be put into a DLL. This way, you can load external DLLs that call the Object DLL to create a dynamic plugin system.
In this object system, every object is a dictionary. Every field is named with a string, and has an ABSTRACT native field. The size of abstract is dependent on the pointer size of the machine. Abstract values can be pointers, primitives, or methods. I won't provide much code for the more advanced stuff (garbage collection, serialization, etc), but I will provide the structs so you can get an idea of how to implement it.
Code:
typedef void* ABSTRACT;
typedef ABSTRACT (*Method)(Object *self, ...); //Methods of an object with cdecl calling conventions
typedef struct {
unsigned char isRef; //"isReference" This one is optional and is for garbage collection and/or type checking purposes!
char *key;
ABSTRACT value;
} Field;
typedef struct {
unsigned int refCount; //Optional field. "reference count" It is for garbage collectors that work on reference counts.
unsigned char protected; //If 1, this object is protected from garbage collection. This is for native access purposes.
unsigned int inheritCount;
char **inherits;
unsigned int fieldCount;
Field *fields;
} Object;
char *_StringClone(char *s) {
char *ret = (char*)malloc(sizeof(char)*(strlen(s)+1));
strcpy(ret, s);
return ret;
}
void ObjectAddType(Object *self, char *type) {
self->inheritCount++;
self->inherits = (char**)malloc(sizeof(char*)*self->inheritCount);
self->inherits[self->inheritCount-1] = _StringClone(type);
}
unsigned char ObjectInstanceOf(Object *self, char *type) {
unsigned int i;
for(i = 0; i < self->inheritCount; i++) {
if(!strcmp(self->inherits[i], type)) {
return -1;
}
}
return 0;
}
unsigned char ObjectSetField(Object *self, char *key, ABSTRACT value) {
unsigned int i;
for(i = 0; i < self->fieldCount; i++) {
if(!strcmp(self->fields[i].key, key)) {
self->fields[i].value = value;
return 0;
}
}
//Field not found, let's create one
self->fieldCount++;
self->fields = (Field*)realloc(sizeof(Field)*self->fieldCount);
self->fields[self->fieldCount-1].key = _StringClone(key);
self->fields[self->fieldCount-1].value = value;
return -1;
}
ABSTRACT ObjectGetField(Object *self, char *key) {
unsigned int i;
for(i = 0; i < self->fieldCount; i++) {
if(!strcmp(self->fields[i].key, key)) {
return self->fields[i].value;
}
}
return NULL; //or throw an exception / set error
}
Method ObjectGetMethod(Object *self, char *key) {
return (Method)ObjectGetField(self, key); //This is exactly the same as GetField, but does the cast for you
}
void ObjectDelete(Object *self) {
unsigned int i;
Method m;
ABSTRACT deconstruct = ObjectGetField(self, "finalize");
if(deconstruct != NULL) {
m = (ABSTRACT)deconstruct;
m(self);
}
for(i = 0; i < self->inheritCount; i++) {
free(self->inherits[i]);
}
for(i = 0; i < self->fieldCount; i++) {
free(self->fields[i].key);
}
free(self->fields);
free(self->inherits);
free(self);
}
ABSTRACT ObjectToString(Object *self) {
//I recommend changing this to return a String object for garbage collection purposes
return self->inherits[0];
}
ABSTRACT ObjectProtect(Object *self) {
self->protected = -1;
return NULL;
}
Object *ObjectCreate() {
Object *self = NEW(Object);
self->refCount = 0;
self->protected = 0;
self->fieldCount = 0;
self->fields = (Field*)malloc(0);
self->inheritCount = 0;
self->inherits = (char**)malloc(0);
ObjectAddType(self, "Object");
ObjectAddType(self, "Serializable");
ObjectSetField(self, "protect", ObjectProtect); //Method that protects the object from garbage collection
// Some default methods to give you ideas on how to improve this object system
ObjectSetField(self, "finalize", NULL); //Deconstructor
ObjectSetField(self, "equals", ObjectEquals); //Test if two objects are equal
ObjectSetField(self, "clone", ObjectClone); //Clone the object
ObjectSetField(self, "toString", ObjectToString); //Convert object to string representation
ObjectSetField(self, "serialize", ObjectSerialize); //Serialize the object (save as a structured byte field)
return self;
}
Just pack these functions in a dll and export the Set/GetField, AddType, and Delete functions. Other DLLs can use these functions to interact with the objects in your program.
And an example code on how to use it
Code:
ABSTRACT PersonSayThis(Object *self, char *msg) {
printf("%s: %s\n", (char*)ObjectGetField(self, "name"), msg);
return NULL;
}
ABSTRACT PersonSayHello(Object *self) {
printf("%s: Hello! I am a person.\n", (char*)ObjectGetField(self, "name"));
return NULL;
}
Object *Person(char *name) {
Object *self = ObjectCreate();
ObjectAddType(self, "Person");
ObjectSetField(self, "name", name);
ObjectSetField(self, "sayThis", PersonSayThis);
ObjectSetField(self, "sayHello", PersonSayHello);
return self;
}
ABSTRACT StudentSayHello(Object *self) {
printf("%s: Hello! I am a student.\n", (char*)ObjectGetField(self, "name"));
return NULL;
}
Object *Student(char *name) {
Object *self = Person(name);
ObjectAddType(self, "Student");
ObjectSetField(self, "sayHello", StudentSayHello); //overwrite
return self;
}
int main() {
Object *person = Person("Bob");
Object *student = Student("Steve");
ObjectGetMethod(person, "sayHello")(person);
if(ObjectInstanceOf(student, "Person")) { //type checking isn't required, but just to make sure it works!
ObjectGetMethod(student, "sayHello")(student); //Uses StudentSayHello
ObjectGetMethod(student, "sayThis")(student, "The sky is falling!"); //Uses PersonSayThis
}
return 0;
}