I'll be going over a simple implementation of object orientation in C, and by the end of this tutorial you'll have set up a simple framework to set up and use simple classes in C. Note: this tutorial will use pointers, function pointers, and etc so don't bother reading if you can't program proficiently in C.
All code is c99 and was compiled under mingw.
Understanding classes and object oriented programming
In object oriented programming, data is private and should not be manipulated directly. Instead, we use member functions that can access the private data. Why might we want to do this? It simplifies and streamlines development and is a more abstract and higher level way of programming. It's "the way" at the moment, with many high schools offering only OOP languages (high schools in my area at least).
In a class, each instance has its own private data set, but uses a table of function pointers for member functions (it does not have its own functions, instead it shares them).
For our implementation, polymorphism and other advanced topics will not be covered. Instead, we'll make a simple String class that is guaranteed to be null terminated (safe).
Since data opaqueness is essential in object oriented programming, make sure to typedef all exported struct pointers to void*.
Setup
For our classes, we're going to make generic allocators, so an instance of any class can be created using the same function. To do this, we'll need to pass a uniform descriptor struct (typedef'd to a void pointer), the same one for each class. Each class will also need to have a pointer to this descriptor struct as its first member, so we can call its constructor/destructor functions without knowing anything else about the class. The second member should be a pointer to the private member block. We also need to know the size of the struct, and size of its private members.
Here's my implementation:
And then we will need to pass the class base to the allocator, along with any other initializers.
Prototype for the deallocator and my implementation of an allocator:
Inside the allocator, _class_type will need to be casted to a Class_s pointer, and you will need to allocate an object of cbClass. The first member should be set as a pointer to the _class_type. If there is a constructor, then call that passing the object and the args. The constructor should allocate memory for its private objects.
Inside the deallocator, _class_object will be checked for a destructor, which will free its own private objects. Then, the object should be released.
Constructor/Deconstructor
Since we don't have a __thiscall convention in C, we have to pass the first parameter as this in each member function (I use self to maintain C++ compatibility and because I use a C++ IDE).
Inside the constructor, we will first allocate memory for our private variables (remember that the first member of the object contains a pointer to the _class_type, which has the size of the private data). The second member of the class should always be a pointer to the private data.
The member function pointers will also need to be setup. In the String_s struct, function pointers to each member function should be assigned.
Here's a sample string constructor (remember constructors and deconstructors don't return values):
And the deconstructor should release the allocated memory for the private objects. This one is simple, so you should be able to implement it yourself.
Member functions
Now we'll finish our simple string class, now that we've got our class system set up. To implement a set function, allocate a buffer _tcslen(tzInitializer) + 1 TCHARs long and set the last one to 0.
To make it more efficient, I decided to allocate memory in blocks of 32, with the max size of the string being 31 to guarantee a null terminator. Then, if I want to concatenate a string, and the length is less than max size, then I can copy it without having to realloc. It also will not allocate if the initializer is smaller than max size, and within a block of it.
For the get function, I decided to go with std::string style and use c_str, which simply returns a const TCHAR pointer to the private member.
Duplicate should simply return a new_class that is initialized to the same c_str as self.
Making it usable
And now to put it together, you can should export the needed class types as void*s.
And to use it:
Appendix
My implementation of a string class: DOWNLOAD
What's next? You can make essentially any class with this, very simply following the given template. Maybe I'll make a follow up tutorial with polymorphism and static class functions (c#/java style) next.
All code is c99 and was compiled under mingw.
Understanding classes and object oriented programming
In object oriented programming, data is private and should not be manipulated directly. Instead, we use member functions that can access the private data. Why might we want to do this? It simplifies and streamlines development and is a more abstract and higher level way of programming. It's "the way" at the moment, with many high schools offering only OOP languages (high schools in my area at least).
In a class, each instance has its own private data set, but uses a table of function pointers for member functions (it does not have its own functions, instead it shares them).
For our implementation, polymorphism and other advanced topics will not be covered. Instead, we'll make a simple String class that is guaranteed to be null terminated (safe).
Since data opaqueness is essential in object oriented programming, make sure to typedef all exported struct pointers to void*.
Setup
For our classes, we're going to make generic allocators, so an instance of any class can be created using the same function. To do this, we'll need to pass a uniform descriptor struct (typedef'd to a void pointer), the same one for each class. Each class will also need to have a pointer to this descriptor struct as its first member, so we can call its constructor/destructor functions without knowing anything else about the class. The second member should be a pointer to the private member block. We also need to know the size of the struct, and size of its private members.
Here's my implementation:
Code
typedef void (*ctor)(void* self, va_list* init);
typedef void (*dtor)(void* self);
typedef struct Class_base {
ctor constructor;
dtor destructor;
size_t cbClass;
size_t cbPriv;
} Class_s;
static const Class_s String_class = {
String_ctor,
String_dtor,
sizeof(String_s),
sizeof(String_priv)
};
typedef struct String_base {
void *Class_type,
*PrivateObjects;
//... member functions
} String_s;And then we will need to pass the class base to the allocator, along with any other initializers.
Prototype for the deallocator and my implementation of an allocator:
Code
void del_class(void* _class_object);
void* new_class(const void* _class_type, ...) {
// remove anonymous type
Class_s* pClass_base = (Class_s*)_class_type;
// allocate class object
void* pObj = (void*)calloc(1, pClass_base->cbClass);
assert_err(pObj, ERR_ALLOC_MEM);
// set first member as class base
*(Class_s**)pObj = pClass_base;
// call appropriate constructor
if (pClass_base->constructor) {
va_list vaArgs = NULL;
// use initializers
va_start(vaArgs, _class_type);
pClass_base->constructor(pObj, &vaArgs);
va_end(vaArgs);
}
return pObj;
}Inside the allocator, _class_type will need to be casted to a Class_s pointer, and you will need to allocate an object of cbClass. The first member should be set as a pointer to the _class_type. If there is a constructor, then call that passing the object and the args. The constructor should allocate memory for its private objects.
Inside the deallocator, _class_object will be checked for a destructor, which will free its own private objects. Then, the object should be released.
Constructor/Deconstructor
Since we don't have a __thiscall convention in C, we have to pass the first parameter as this in each member function (I use self to maintain C++ compatibility and because I use a C++ IDE).
Inside the constructor, we will first allocate memory for our private variables (remember that the first member of the object contains a pointer to the _class_type, which has the size of the private data). The second member of the class should always be a pointer to the private data.
The member function pointers will also need to be setup. In the String_s struct, function pointers to each member function should be assigned.
Here's a sample string constructor (remember constructors and deconstructors don't return values):
Code
void String_ctor(void* self, va_list* init) {
// remove anonymous types
String_s* sSelf = (String_s*)self;
Class_s* pClass_base = (Class_s*)sSelf->Reserved[0];
// allocate memory for private members
sSelf->Reserved[1] = calloc(1, pClass_base->cbPriv);
assert_err(sSelf->Reserved[1], ERR_ALLOC_MEM);
// initialize exported from class
sSelf->duplicate = pClass_base->duplicate;
// initialize rest of function table
sSelf->c_str = String_c_str;
sSelf->set = String_set;
// get initializer and copy
const TCHAR* tzInit = va_arg(*init, const TCHAR*);
sSelf->set(self, tzInit);
}And the deconstructor should release the allocated memory for the private objects. This one is simple, so you should be able to implement it yourself.
Member functions
Now we'll finish our simple string class, now that we've got our class system set up. To implement a set function, allocate a buffer _tcslen(tzInitializer) + 1 TCHARs long and set the last one to 0.
To make it more efficient, I decided to allocate memory in blocks of 32, with the max size of the string being 31 to guarantee a null terminator. Then, if I want to concatenate a string, and the length is less than max size, then I can copy it without having to realloc. It also will not allocate if the initializer is smaller than max size, and within a block of it.
For the get function, I decided to go with std::string style and use c_str, which simply returns a const TCHAR pointer to the private member.
Duplicate should simply return a new_class that is initialized to the same c_str as self.
Making it usable
And now to put it together, you can should export the needed class types as void*s.
Code
static const void* String_type = (const void*)&String_class;
typedef String_s* String;And to use it:
Code
String s = (String)new_class(String_type, _T("sample"));
printf("%s", s->c_str(s));Appendix
My implementation of a string class: DOWNLOAD
What's next? You can make essentially any class with this, very simply following the given template. Maybe I'll make a follow up tutorial with polymorphism and static class functions (c#/java style) next.


![[Image: 9N6Aq1f.gif]](https://i.imgur.com/9N6Aq1f.gif)
