Brief:
This is a re-approach of an article I wrote, but this time it will be a tutorial.
I will begin with a massive introduction before we write our first base "class".
I won't detail how to setup an IDE, this works with any C99 compiler so you should be able to set up an IDE yourself with another tutorial and then get to this.
If you enjoy Ruby or Objective-C, then this tutorial should be right up your alley as the coding conventions are Ruby/Obj-C inspired. If you enjoy C++ then stick with it, but read on if you're interested in the lower-level handling of C++ classes.
If you know an OOP language (Such as Ruby) but don't know C, then this should give you a good way to move on and learn C, so perhaps this will inspire some of you to explore the language!
Trivia: The Linux kernel is written in C but with an OOP flavour, as is the SDL library. My C coding style will mimic the SDL C style a bit, so if you're planning on writing a game with SDL, this might help you understand the library's inner workings.
The Return of Object Oriented C
Part 1: Introduction and Complaints
Before the tutorial begins, here's some points that I want to cover;
1: Use C99 Please
...Please...If you're not going to use C++ then please use C99. All this code will be C++ compatible as much as possible (Pointer casting might fail) but there's no guarantee there.
My last tutorial was GCC/Clang only because it used some GCC extensions, now that the VC compiler has C99 support (Barely has C99 support) I can now care about that, so download Visual Studio 2013 and create a main.c file because you can do that now.
The BIG #1 difference between C90 and C99 is this:
What this means is: You now need to be careful with those goto statements you shouldn't be using because the stack is even more complicated to follow in source
And: You won't have blocks thrown in whenever you need to deal with the stack
Basically, the only concern about stack management left is the object pointer problem.
2: C++ sucks in a lot of things
So that last tutorial I did was written before I really had a complete grasp of C++ (I had been using C a lot), but I tried to emulate C++ as much as I could to get into familiar grounds. So what I ended up doing was inheriting the problems with C++, which is never good.
The first mistake I did was specific to my needs to reduce memory consumption; I had pointers to base classes. Doing that added a lot of complexity and opportunities for things to go horribly wrong (Luckily in the production code I only got 1 phone call about things going wrong and it was this pointer problem wearing it's ugly head on the Apple ARM 64bit CPU).
So this time, it's OOP C without the horrors of C++ knowingly coded into it.
Let's complain about C++ for a bit. C++ is technically around the same level as C in terms of binary execution, but in terms of coding style I'd say it's a level higher.
My first gripe is the new and delete keyword, at first glance it's a replacement of the library-dependant malloc and free functions, it moves heap memory allocation to the language level and not the operating system level, it is now even harder to write your own memory management systems and if you want to do it then you have to include the <new> header to gain the new( memorySpace )xiClass(); capability, which is needed entirely for class constructors to work properly with custom memory allocators.
Then there's that problem with having object arrays, malloc( sizeof( object ) * arrayLength ) is replaced with new object[64], but to call the deconstructors of the array you need to now use the delete[] keyword, so you need to keep track of what memory blocks are single objects or an array of objects.
Why would you want to do custom memory allocation? It's faster when you do your own, unsafe allocations rather than letting the operating system do it and if you're really hard-core, you can't do your own memory compression with C++'s default memory allocation setup.
A lot of these complaints are only relevant if you're doing some crazy shit, so if you're part of the 99% of people who don't need to worry about that stuff, then what's coming up is pretty much for your own interest and might not be too useful for you.
3: The Convention
When I first approached OOP C it was the mindset of "I need to adhere to my C rules but implement C++ features", totally not a good idea, the code does end up messy eventually. I now believe it's better to implement something that is right for the language, rather than right for what you know, so I now propose a new model that mixes the guts of C++ with the flavour and freedom of C.
Every C programmer will tell you "C feels pure", and it is, the language gives you nothing! So in that spirit, let's do the object oriented paradigm but let the programmer decide how they use it and in a safe way, totally against the spirit of C++ which pulls the responsibility into the language and against the rigidness of my original OOP C thread which forces objects to take a set design principle.
I'm also going to say: OOP is not perfect, don't follow it strictly for the sake of it, C does not have private variables, don't feel the need to add that in (Which my original tutorial did).
4: Love your header files
This should piss off a few programmers.
Header files is the 1 thing everyone hates about C/C++ (Everyone but me), it's NOT part of the C/C++ language, it's part of the pre-processor. What the header files do is forward-declare functions so they can be accessed by the rest of the source below, this is an aspect that is needed due to the top-down compiling nature of C and C++, it also means that you can create nice API systems, with Java if you need to create a lovely API you tend to make a binding class file and ship that around with your library, think of C/C++ source files as individual programs that need to import library APIs through header files!
Everyone says Apple are on a mission to remove header files from Objective-C, what's the alternative? Make everything public. You'll be seeing a lot more use of the static keyword.
You have a lot of control with how the contents of header files are used and you will see that soon...
But I do believe everyone would stop complaining about header files once they understand what makes them work and why they are currently needed.
Also, this tutorial will use a lot of header files.
5: Re-introducing Classes in C!
So this is the big problem with my original model:
So straight away it requires function calls and other stuff to even get a single class working.
A better way that makes way more sense is to not use pointers at all.
Now it's time to get rid of the library memory management entirely. This is easy, we used to have xiBase_t * NewBase(), you can see that it's C++ inspired, inside that function would be a malloc() and then normal construction.
Here's a better function:
Cool, all those mallocs have been moved to the stack. Or we can use malloc and make a new object on the heap.
We can also move the malloc into an optional function that is intended for this object type.
So that's the biggest different with my old C-with-classes system in brief. Onto creating our very own base class!
Coming up next...Creating a base class!
This is a re-approach of an article I wrote, but this time it will be a tutorial.
I will begin with a massive introduction before we write our first base "class".
I won't detail how to setup an IDE, this works with any C99 compiler so you should be able to set up an IDE yourself with another tutorial and then get to this.
If you enjoy Ruby or Objective-C, then this tutorial should be right up your alley as the coding conventions are Ruby/Obj-C inspired. If you enjoy C++ then stick with it, but read on if you're interested in the lower-level handling of C++ classes.
If you know an OOP language (Such as Ruby) but don't know C, then this should give you a good way to move on and learn C, so perhaps this will inspire some of you to explore the language!
Trivia: The Linux kernel is written in C but with an OOP flavour, as is the SDL library. My C coding style will mimic the SDL C style a bit, so if you're planning on writing a game with SDL, this might help you understand the library's inner workings.
The Return of Object Oriented C
Part 1: Introduction and Complaints
Before the tutorial begins, here's some points that I want to cover;
1: Use C99 Please
...Please...If you're not going to use C++ then please use C99. All this code will be C++ compatible as much as possible (Pointer casting might fail) but there's no guarantee there.
My last tutorial was GCC/Clang only because it used some GCC extensions, now that the VC compiler has C99 support (Barely has C99 support) I can now care about that, so download Visual Studio 2013 and create a main.c file because you can do that now.
The BIG #1 difference between C90 and C99 is this:
C:
<div class="c" id="{CB}" style="font-family: monospace;"><ol>// DOES NOT COMPILE WITH C90!!!
CallFunction();
<span style="color: #993333;">int myNum = <span style="color: #cc66cc;">30;
C:
<div class="c" id="{CB}" style="font-family: monospace;"><ol>/* Compiles with C90 */
<span style="color: #993333;">int myNum;
CallFunction();
myNum = <span style="color: #cc66cc;">30;
What this means is: You now need to be careful with those goto statements you shouldn't be using because the stack is even more complicated to follow in source
And: You won't have blocks thrown in whenever you need to deal with the stack
Basically, the only concern about stack management left is the object pointer problem.
2: C++ sucks in a lot of things
So that last tutorial I did was written before I really had a complete grasp of C++ (I had been using C a lot), but I tried to emulate C++ as much as I could to get into familiar grounds. So what I ended up doing was inheriting the problems with C++, which is never good.
The first mistake I did was specific to my needs to reduce memory consumption; I had pointers to base classes. Doing that added a lot of complexity and opportunities for things to go horribly wrong (Luckily in the production code I only got 1 phone call about things going wrong and it was this pointer problem wearing it's ugly head on the Apple ARM 64bit CPU).
So this time, it's OOP C without the horrors of C++ knowingly coded into it.
Let's complain about C++ for a bit. C++ is technically around the same level as C in terms of binary execution, but in terms of coding style I'd say it's a level higher.
My first gripe is the new and delete keyword, at first glance it's a replacement of the library-dependant malloc and free functions, it moves heap memory allocation to the language level and not the operating system level, it is now even harder to write your own memory management systems and if you want to do it then you have to include the <new> header to gain the new( memorySpace )xiClass(); capability, which is needed entirely for class constructors to work properly with custom memory allocators.
Then there's that problem with having object arrays, malloc( sizeof( object ) * arrayLength ) is replaced with new object[64], but to call the deconstructors of the array you need to now use the delete[] keyword, so you need to keep track of what memory blocks are single objects or an array of objects.
Why would you want to do custom memory allocation? It's faster when you do your own, unsafe allocations rather than letting the operating system do it and if you're really hard-core, you can't do your own memory compression with C++'s default memory allocation setup.
A lot of these complaints are only relevant if you're doing some crazy shit, so if you're part of the 99% of people who don't need to worry about that stuff, then what's coming up is pretty much for your own interest and might not be too useful for you.
3: The Convention
When I first approached OOP C it was the mindset of "I need to adhere to my C rules but implement C++ features", totally not a good idea, the code does end up messy eventually. I now believe it's better to implement something that is right for the language, rather than right for what you know, so I now propose a new model that mixes the guts of C++ with the flavour and freedom of C.
Every C programmer will tell you "C feels pure", and it is, the language gives you nothing! So in that spirit, let's do the object oriented paradigm but let the programmer decide how they use it and in a safe way, totally against the spirit of C++ which pulls the responsibility into the language and against the rigidness of my original OOP C thread which forces objects to take a set design principle.
I'm also going to say: OOP is not perfect, don't follow it strictly for the sake of it, C does not have private variables, don't feel the need to add that in (Which my original tutorial did).
4: Love your header files
This should piss off a few programmers.
Header files is the 1 thing everyone hates about C/C++ (Everyone but me), it's NOT part of the C/C++ language, it's part of the pre-processor. What the header files do is forward-declare functions so they can be accessed by the rest of the source below, this is an aspect that is needed due to the top-down compiling nature of C and C++, it also means that you can create nice API systems, with Java if you need to create a lovely API you tend to make a binding class file and ship that around with your library, think of C/C++ source files as individual programs that need to import library APIs through header files!
C:
<div class="c" id="{CB}" style="font-family: monospace;"><ol><span style="color: #339933;">#include <stdio.h> // Don't need to do this
<span style="color: #993333;">int [url=http://www.opengroup.org/onlinepubs/009695399/functions/printf.html]printf[/url]( <span style="color: #993333;">const <span style="color: #993333;">char *, ... ); // You can just do this!
Everyone says Apple are on a mission to remove header files from Objective-C, what's the alternative? Make everything public. You'll be seeing a lot more use of the static keyword.
You have a lot of control with how the contents of header files are used and you will see that soon...
But I do believe everyone would stop complaining about header files once they understand what makes them work and why they are currently needed.
Also, this tutorial will use a lot of header files.
5: Re-introducing Classes in C!
So this is the big problem with my original model:
C:
<div class="c" id="{CB}" style="font-family: monospace;"><ol><span style="color: #993333;">typedef <span style="color: #993333;">struct xiBase_s {
<span style="color: #993333;">char byte;
} xiBase_t;
<span style="color: #993333;">typedef <span style="color: #993333;">struct xiSuper_s {
xiBase_t * super;
} xiSuper_t;
<span style="color: #993333;">int main( <span style="color: #993333;">int argc, <span style="color: #993333;">char ** argv ) {
xiSuper_t object;
object.<span style="color: #202020;">super->byte = <span style="color: #cc66cc;">128; // Won't work, never instansiated the pointer
<span style="color: #b1b100;">return <span style="color: #cc66cc;">1;
}
So straight away it requires function calls and other stuff to even get a single class working.
A better way that makes way more sense is to not use pointers at all.
C:
<div class="c" id="{CB}" style="font-family: monospace;"><ol><span style="color: #993333;">typedef <span style="color: #993333;">struct xiBase_s {
<span style="color: #993333;">char byte;
} xiBase_t;
<span style="color: #993333;">typedef <span style="color: #993333;">struct xiSuper_s {
xiBase_t super;
} xiSuper_t;
<span style="color: #993333;">int main( <span style="color: #993333;">int argc, <span style="color: #993333;">char ** argv ) {
xiSuper_t object;
object.<span style="color: #202020;">super.<span style="color: #202020;">byte = <span style="color: #cc66cc;">128; // Works perfectly fine!
<span style="color: #b1b100;">return <span style="color: #cc66cc;">1;
}
Here's a better function:
C:
<div class="c" id="{CB}" style="font-family: monospace;"><ol>xiBase_t * Base_Init( <span style="color: #993333;">void * <span style="color: #993333;">const self ) {
<span style="color: #b1b100;">if ( !self ) {
<span style="color: #b1b100;">return <span style="color: #cc66cc;">0;
}
( ( xiBase_t * )self )->byte = <span style="color: #cc66cc;">0;
<span style="color: #b1b100;">return self;
}
xiBase_t object;
Base_Init( &object );
C:
<div class="c" id="{CB}" style="font-family: monospace;"><ol>xiBase_t * <span style="color: #993333;">const object = Base_Init( malloc( <span style="color: #993333;">sizeof( xiBase_t ) ) );
free( object );
We can also move the malloc into an optional function that is intended for this object type.
C:
<div class="c" id="{CB}" style="font-family: monospace;"><ol><span style="color: #993333;">typedef <span style="color: #993333;">struct xiBase_s {
<span style="color: #993333;">char byte;
_Bool isHeap;
<span style="color: #993333;">void ( * free_f )( <span style="color: #993333;">void * );
} xiBase_t;
xiBase_t * Base_Alloc() {
xiBase_t * <span style="color: #993333;">const self = malloc( <span style="color: #993333;">sizeof( xiBase_t ) );
memset( self, <span style="color: #cc66cc;">0, <span style="color: #993333;">sizeof( xiBase_t ) ); // Optional clear the memory, could have used calloc()
self->isHeap = <span style="color: #cc66cc;">1; // We can do something like this now!
self->free_f = &free; // Or this to let the object manage it's own deallocation
<span style="color: #b1b100;">return self;
}
xiBase_t * <span style="color: #993333;">const object = Base_Init( Base_Alloc() );
object->free_f( object ); // There's a better way for this too!
So that's the biggest different with my old C-with-classes system in brief. Onto creating our very own base class!
Coming up next...Creating a base class!