OOC: Methods and Classes

Hello, advice before start reading!  take a long breath, and check the references of the post while reading. and don’t try this at homes 😉

let’s implement a String Class In C !! What we need to implement a String data type?!
all what we need is a dynamic buffer to hold the text and when the string is deleted we need to free the buffer.

Goal Of The Post

Be able to write these two lines of code

void * a = new(String, "Text");
delete(a);

Flashback !!

Let us remember something from the previous post about new() and delete().

  • new():

    • new was responsible for creating an object, and also know that type of object it’s creating as it has its description in the first parameter.
    • Problem Here !!
      • Assume that we are creating just 100 different data types, then we’ll handle this in new()  by using multiple of if statements which is a great problem, as new()  must contain the code for each data type explicitly.
  • delete():
    • delete was responsible for freeing the memory and checking the available resources.
    • Problem Here !!
      • it acts differently depending on the type of the object for instance String, delete with string type must the buffer.

Let’s think about the solution, as we noticed new() need a description for the object to be able to allocate it,  we can simply think about making new and delete GENERIC for any data type and make a type-specific functions we can name them (Constructor and Destructor) that can be used in initialization and as they do not change we can pass them to new and delete as a part of the type description.

  • IMP note: Note that constructor and destructor are not responsible for acquiring and releasing the memory for an object itself — this is the job of new() and delete().

the constructor is invoked by new() and just initialize the memory area allocated by new() and delete()  calls the destructor that destructs the initialization made by constructor before delete() recycles the memory area allocated by new().

Then, how can delete() locate the destructor function

struct type {
size_t size; /* size of an object */
void (* dtor) (void *); /* destructor */
};
struct String {
const void * destroy;
char * text; /* dynamic string */
};

if we noticed the pointer named (destroy) in the previous code snippet, What Should This pointer point to ?!!

If all we have is the address of an object, this pointer gives us access to type-specific information for the object, such as its destructor function. but what if we have multiple functions (compare, clone, .. etc), so this pointer will point to a table of function pointers.

struct Class {
size_t size;   /*length of allocated space*/
void * (* ctor) (void * self, va_list * app);  /*to initialize memory*/
void * (* dtor) (void * self); /*to free the memory */
void * (* clone) (const void * self); /*make a copy of the object*/
int (* differ) (const void * self, const void * b); /*comparison*/
};
struct String {
const void * class; /* must be first */
char * text;
};

as we see many object may share the same type descriptor, the same amount of allocated memory and the same methods can be applied to all of them. All objects that have the same type descriptor are named Class.

ohhh, that’s enough for today, to be continued in the next post In Shaa’ ALLAH  with Polymorphism and Dynamic Linkage.

new.h

#ifndef NEW_H_
#define NEW_H_
#include <stddef.h>
void* new(const void *class,...);
void delete(void* item);

void *clone(const void *slef);
int differ(const void *self, const void* b);
size_t sizeOf(const void* self);

#endif /* NEW_H_ */

new.r

this is the representation file, check first reference.

#ifndef CLASS_R
#define CLASS_R

#include <stdarg.h>
#include <stdio.h>

struct Class{

size_t size;
 void *(*ctor)(void *self, va_list *app);
 void *(*dtor)(void *self);
 void *(*clone)(const void *self);

 int(*differ) (const void *self, const void *b);
};
#endif

new.c

#include <assert.h>
#include <stdlib.h>
#include <stdarg.h>

#include "new.h"
#include "new.r"

void* new (const void* _class, ...)
{
 const struct Class* class = _class;
 void *p = calloc(1,class->size);
 assert(p);
 *(const struct Class **)p = class;
 if(class->ctor)
 {
 va_list ap;
 va_start(ap,_class);
 p = class->ctor(p, &ap);
 va_end(ap);
 }
 return p;
}

void delete(void *self)
{
 const struct Class ** cp = self;
 if(self && *cp && (* cp)->dtor)
 self = (* cp)->dtor(self);
 free(self);
}

String.h

#ifndef STRING_H_
#define STRING_H_

extern const void *String;

#endif /* STRING_H_ */

String.c

#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

#include "String.h"
#include "new.h"
#include "new.r"
struct String
{
 const void *class;
 char *text;
};

static void *String_ctor(void *_self, va_list* app)
{
 struct String *self = _self;
 const char* text = va_arg(*app, const char*);

self->text = malloc(strlen(text)+1);
 assert(self->text);
 strcpy(self->text,text);
 return self;
}

static void *String_dtor(void *_self)
{
 struct String *self = _self;
 free(self->text),self->text = 0;
 return self;
}

main.c

#include <stdio.h>
#include <stdlib.h>

#include "String.h"
#include "new.h"

int main(void) {
void * str = new(String, "Text");
delete(str);
return 0;
}

References: