Welcome Guest ( Log In | Register )

[ Big| Medium| Small] -



Post new topic Reply to topic  [ 4 posts ] 
    Xilef
  Wed Apr 30, 2014 11:09 pm
User avatar
Staff

Big Dumb Guy

Location: UK
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:
Expand to see the code.


Expand to see the code.


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!

Expand to see the code.


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:
Expand to see the code.


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.
Expand to see the code.

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:
Expand to see the code.

Cool, all those mallocs have been moved to the stack. Or we can use malloc and make a new object on the heap.
Expand to see the code.


We can also move the malloc into an optional function that is intended for this object type.
Expand to see the code.


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!


Top Top
Profile      
 

    Xilef
  Thu May 01, 2014 1:25 am
User avatar
Staff

Big Dumb Guy

Location: UK
Creating a base class

Start off by making a header file (Don't complain) and call it "Object.h".

In here we will create the declaration of our base class and the methods (Class functions).

Object.h

Straight away add in the header guards, because header files are part of the pre-processor and not the language, we need to tell the pre-processor how to handle the header file.
Expand to see the code.


Why guard?

People hate header files, guarding is one of the reason, but why do we need to do it?

Imagine we have this header file:
Expand to see the code.

Seems normal enough, let's look inside Number.h...
Expand to see the code.

Okay, so Thing.h includes Number.h that includes Thing.h that includes Number.h...We have a dependancy loop!

That #ifndef block basically says "If this pre-processor value is not defined, then define it and use the code in this block".
So when we include Number.h and if it has the guard __NUMBER_H__ then when we first include it, that __NUMBER_H__ is now defined and the code within the block can be skipped.

Trivia: Some compilers have the non-standard #import pre-processor command (It's standard for Objective-C), this #import does the header guarding automatically with the header file name without having to open up the header file and check, which is handy, but what if you have something you wanted outside the guard? ;)

Define the base class

Back to that Object.h file we made...

Because this is C and not C++, we don't have the class keyword, instead we take advantage of C structures (Which is what C++, Ruby and Objective-C classes are based on).

In some languages it's standard practice to prefix your classes with something (Apple's Objective-C uses NS infront of their classes), I will prefix my classes with the letters "xi" for Xilef, you will know it's a class when you see the letters xi infront of the type!
Expand to see the code.

If you're coming from C++, think of the "typedef struct X_s {} X_t;" equivalent to "class X {};", the forward-declaring is the same too (typedef struct X_s X_t versus class X).

If you're coming from any other language, the typedef basically defines a new type, in C90 we would use "stuct xiObject_s {}" EVERYWHERE that we needed the structure (That's a lot of duplicated code!), by using typedef we can now simply use "xiObject_t" and forget about re-writing the struct and it's members each time.

Trivia: The Ruby base class internally looks like this;
Expand to see the code.
Notice that it doesn't use typedefs for the structs, but it has used one for the rb_classext_t type (We can tell because they kindly added _t at the end of the type name, something that this tutorial will be doing).

Deciding what members we need in base

This is a base class, so the idea is that every class inherits from this class. What do we put in this class then? We could do what Ruby does and put in a reference to the super class (Class that inherited this one), but that's not really needed for how we will deal with inheritance.

I'm going to cheat and tell you that we're going to need a virtual method and a variable for keeping a reference count, both for memory management reasons!

If you come from an OOP language you should know this, but virtual methods are class methods that can be called no-matter what alias the class is masquerading as.

So I can have an animal, point at it and say "Cat, speak!", with standard OOP that animal is going to meow, no matter what. If I then point at it and say "Dog, speak!" it's going to bark at you, even if the animal is a dolphin.

With virtual methods we can override the "speak" method and make it unique with every class, so the default speak of an animal is zero, there is no such physical concept of an animal so why should it need to speak? When we derrive cat from the animal we can replace the speak method with one that says "meow", when we derrive dog from the animal we can replace speak with "bark".

Then we have this for a cat: "Cat, speak!" [meow!], "Animal, speak!" [meow!] "Dog, speak!" [meow!]
So even when we call that cat a dog and tell it to speak, it will say "meow!" because dog, cat and animal have the same virtual method "speak" with the same signature.

We're going to copy C++'s way of doing virtual methods as it is the easiest way (And Ruby does this internally with struct method_table_wrapper *m_tbl_wrapper, it uses pointers so some memory construction needs to be done).

Shove a vtable structure into the class;
Expand to see the code.

The vtable is where we will store the pointers to the functions that the virtual functions will call, we can replace this pointer each time we initialise a class (C++ does this internally with the new keyword) and handle it later.

That Dealloc_f function pointer will be useful for memory management later!

What is reference counting?

Reference counting is a memory management model that uses a number to remember how many pointers are pointing to the object instance, the idea is that when that number hits zero the object must be destroyed, this is generally for cleaning up heap memory, when our object is on the stack this must not be used otherwise the deallocation will crash the program (Can't dealloc stack memory).
This reference counting thing is optional (It's more useful for multi-threaded programming and leak detection, handy for big programs)

I suggest we wrap this reference counter in a structure that is dedicated to memory. We shall call this structure "memory"!.

Expand to see the code.


So that's what our class will look like. We can now include this anywhere and gain access to using this class.

Class Methods

But truthfully, this class is useless. Doesn't do anything right now.

Time to give it some methods. Well, functions technically, but this is OOP style so we still call these methods.

We need an initialising method that sets up the class, so add the function to the bottom of the header file:
Expand to see the code.

To initialise the object, we need to pass in the memory that contains the object, we also return the resulting pointer, so if something goes wrong during initialisation we have the opportunity to return a null pointer.

While we're here, let's define the functions for memory management. Add the following functions to the end of the header file.
Expand to see the code.

If you're doing this in C++, you might want to think about using void pointers as the self pointer and casting to the correct type in the implementation, otherwise you'll be casting each time you want to use a super class' method.

So that's the declaration done!

Object.h
Expand to see the code.


Time to Implement!

Now we define the behaviour. Make a new source file called Object.c and write this line at the top:
#include "Object.h"

That copies in our object's information into this source file.

Copy the methods into the source file and add in the braces as such:
Expand to see the code.

Aha, we have some missing return types there! We'll deal with this 1 method at a time.

Initialise Method

This method needs to set the starting data of this class. For now, let's cheat and use the stdlib memory management model.

Add this line below the object.h include:
#include <stdlib.h>
That will copy in the standard C library function declares to this source file, so we can now allocate memory with malloc(), free memory with free() and resize memory with realloc().

The initialise method needs to set the vtable, this is an important requirement that must be done here!

But first, do a check to make sure we're not being given a null pointer to initialise (Always good to do that here!).

Expand to see the code.


Memory Allocation

We have a cool opportunity here to move potential heap allocations into the class method itself.

First, add an include for <string.h> after the include for <stdlib.h>
The string.h file comes with more memory management functions intended for handling c-strings (Character arrays that end with 0), it comes with the memset function that we can use instead of a calloc function.

Then write the following into the Object_Alloc() method:
Expand to see the code.


If you are daring, you can move the current Dealloc_f and retainCount setting from the Init method into the Alloc method, but you need a way for marking the memory as "on the heap", otherwise no class will know how to deal with it and the class will be filled with garbage data to begin with.

Dealloc - Our virtual method

Remember that virtual method stuff? We use it here. Because a class could have it's own way of destroying it's memory we should use the vtable when the generic Object_Dealloc() method is called.
For other classes that are on the stack, we would write a *_Terminate() method, but right now for reasons of "just-in-case", we are using a virtual method here.

Expand to see the code.

There are some important things to note: The Dealloc_f function pointer is set in the Object_Init method, this call presumes that the class has been initialised, the other thing is that the memory is cleared in the Object_Alloc method, we have no-way of knowing if this object is on the heap or the stack, so anything on the stack that calls this method will crash the program (Can't free stack memory).

Stack memory is popped automatically at the end of it's block, so we don't actually need to worry about calling *_Dealloc to classes on the stack (But we will need to worry about calling a terminate method later on!).

This is sensitive stuff, later on I'll describe a way to separate the heap memory management from the class' internal data so we can safely deal with memory, which will involve converting a certain method to a virtual method (Patching C code without breaking things, how exciting!).

Reference Counting

Finally, we have those last two methods that are to do with the reference counting memory model I described above.
Retain is simple, every time we call Object_Retain we want to say "Yes, we have a new pointer to this object!".
Expand to see the code.


Release is where the magic happens (And it gets more magical in the future).

Expand to see the code.


Very cool.

This is the entirety of Object.c:
Expand to see the code.


TIME TO TEST IT!!!

Add a file main.c and write this in it:
Expand to see the code.

Time to write something cool...What could we write?

Here's some example of heap and stack usage of this class system:
Expand to see the code.

I suggest you put a break-point at that main function and then step through the code line-by-line in your debugger and inspect each of those object variables, check their reference counts to see the memory management in use and check the data that the pointers point to after the Dealloc and Release methods are used.


Extra:

So WTF are pointers, anyway?

Pointers are numbers that are positions in your computer memory. Pointers themselves are not objects or data, they are integers.

In Java, Ruby and C#, all class instances are pointers, people might be saying these languages don't have pointers, but in truth these languages are 100% pointers, there are just automatic memory management systems in-place to tidy up the lost memory references, these three languages have a garbage collector.

What the garbage collector does for these languages is check the reference count variable for all the classes, and if it's 0 then it frees up the memory, pretty much a slow, automatic version of our manual memory management system.

You can do crazy stuff with pointers in C and C++, for example; With arrays you can have myArray[index], which dereferences myArray as a pointer, or you can have index[myArray], which does the same thing. Why? That is because:
myArray[index] is the same as *( ( arrayType_t * )( myArray + index ) )
And index[myArray] is the same as *( ( arrayType_t * )( index + myArray ) )

Weird, right? Well that's because pointers are just numbers.

However, when de-referencing pointers, modern operating systems check if the pointer you're dealing with is related to the application that is dereferencing.
If it isn't, then crash time!

You can see why people hate pointers, but they can be massively, massively useful for linked-lists and array systems, doing a linked-list in Java involves a global array that keeps track of index positions, essentially you have to write your own mini-memory-pointer system for it.



Coming up...Making the memory even safer! Going complicated!


Top Top
Profile      
 

    Xilef
  Thu May 01, 2014 4:17 pm
User avatar
Staff

Big Dumb Guy

Location: UK
Making the memory even safer! Going complicated!

Most modern languages use a secondary system for managing objects (Usually for garbage collection, but Ruby and Objective-C use a secondary list for object messaging), C was never designed with objects in mind, so to make things easier we need to also introduce a secondary list to help manage our heap memory.

The Object.h file will not change, we will keep the same definition for the class, but we'll add some heap management code into the class file.

Make a new header file called HeapUtil.h, this is where our heap management system will go.
This header file will not have an implementation file with it, rather we'll write some static functions here intended to be used by our base class.

We'll start with two static variables, one that holds the list and one for the current size of the list.

Expand to see the code.

That <stddef.h> include gives us access to the size_t type, this is a type that describes memory sizes, it should be used whenever you're dealing with array indexing.
On 64bit machines, most compilers will set size_t to be an unsigned 64 bit integer, with 32bit machiens, it's usually an unsigned 32 bit integer.

That stdlib is like before, it gives us access to realloc(), free(), memmove and qsort, which are useful for memory management.

Those static keywords mean "These variables will only be relevant to this source file", so those heapList and heapListLen globals will be uniquely available to the source file that includes this header.
Remember, header includes are just copying code in, this is not a "feature" of header files, if you wanted you can put static variables into your source .c files! The reason we're using a header file here is because it helps you learn about how headers are used and we might want multiple heap managers in the future (Probably won't, though!).

That void ** type might look confusing for those who are new to pointers, remember that pointers are just numbers! So void * is a number, the second pointer * is saying that this is a number that points to a place in computer memory that has another number.
We can use this with the array indexing trick to treat this as a dynamic array.
heapList[index] will reference a void *!

Static Functions

Like static variables, static functions exist only in the scope of the source file that has them. If you think of .c files as compiling to their own programs with .h files as the API binding, when something is static then there is no way for the .h file to access it, so no other .c files will be able to use that function/variable, this is good for managing name reserves, so we can make a static function called "ABC()" and somewhere else in the program have another function called "ABC()" and the compiler won't complain about it (As the static one is hidden!).

As this is a list, there are some set actions we need:
  • Compare - For comparing the positional relationship between two items
  • Add - For adding an item to the list (We also sort here!)
  • Has - For checking if an item is in the list
  • Remove - For removing a given item
Our functions will be prefixed with the word "HeapList" so we know these functions are to do with our heap management system.

Compare

The compare function needs a specific signature for use with the qsort function. Remember the static keyword too to keep this function available only in the sources that include the header!
Expand to see the code.

This function returns an integer that describes the positional relationship between the left and right arguments. Left and right are expected items in the list, and if you remember this is a list of void * and pointers are just numbers, so we can make this easy!
Expand to see the code.

-1 tells that left is "less than" right, +1 is that left is "greater than" right, so we can use those operators inside an if statment. Very easy!

Add

Expand to see the code.


This pushes a given item onto the end of the list and then sorts the list using our HeapListCompare() function.

It has no return value and it expects an item to be added to the list.

Here's the full function:
Expand to see the code.

The first part up to the end of that if statement is for pushing the item to the end of the list.
If you read it slowly, you will see that first it gets the new size of the list and stores it in biggerHeapListLen, it then reallocates the memory at heapList to make it the size of a dereferenced item of biggerHeapList (A void *) multiplied by how big we now want the list (biggerHeapListLen!).

It is important that heapList and heapListLen start off initialised as zero, if heapList was not initialised to 0 then the realloc will attempt to modify a random position in computer memory, very bad! When realloc tries to reallocate a null pointer (zero) it actually calls malloc and makes a brand new section of memory. Nice!

The final part of realloc is to check that it worked successfully, if it didn't then biggerHeapList will be a null pointer, so we can just throw in an if statement checking that.

One thing to note, the original heapList pointer has not changed during this, we assigned a new pointer with biggerHeapList, heapList now points to old, invalid memory! What we need to do is first set the end of the biggerHeapList to our item's value and then assign the heapList and heapListLen variables to the correct values.

That's how an array push works!

Let's use this opportunity to sort the list, C comes with a lovely qsort function, it's performance depends on the compiler's implementation of the algorithm, but from plenty of tests it's actually faster than the C++ recommended std::sort. It's bizarre that C++ fans recommend std::sort so strongly over qsort, but there you go. We're in C right now so no-one can complain.

This qsort function takes the list that needs to be sorted, how big the list is, then the size in bytes of each element in the list (We derefence the list itself to get a void * for the size check here), and finally it takes a function to do the list comparison, this is where our HeapListCompare() function first gets used.

Has

So now it's time for a search algorithm, for finding if an item exists in the list.

I'm going to use a C99 boolean here because we haven't used one yet.

Expand to see the code.

The _Bool is a C version of the C++ bool type, it is an integer of any size that will automatically set itself to the values 0 or 1, false or true respectively.
I don't use the <stdbool.h> header as it does some defines that makes the C boolean look like the C++ boolean, and I disagree with doing that, we don't want C to pretend to be C++!

The outIndex pointer is interesting, the idea is that we can optionally fill the contents of that pointer with the index of the item if it does indeed exist.

First thing to do is check if the item or the list even exists! We don't want null pointer crashes. We can presume that if the item we're looking for doesn't exist or the list doesn't exist then the non-existant list definitely does not have the non-existant item or similar!

Expand to see the code.
Rememer, 0 means false, 1 means true.

The rest of this function implements a binary search algorithm.
Expand to see the code.

I hope you all brushed up on your descision mathematics!

This algorithm uses two boundaries for a search-space and shrinks the boundaries by half each time the direction of the desired item is found (HeapListCompare() is used here! Handy!)

If we never find the item, then eventually our boundaries will cross each other, at that point we can return false (return 0 at the end), so that's what the while () statement is checking.

The cursor is the midpoint between the two boundaries, most compilers will substitute an integer divide-by-2 with a bit-shift left 1, I've written it myself.
Expand to see the code.
This is some binary arthimatic here. You can try this yourself if you know binary, write doing a binary number, find it's denary value, shift all the bits to the right by 1 space (Removing the far right bit) and find the new denary value, it will be half the original (Rounded down, whole numbers!).

Remember the compare function from before? That returns zero if the two items are equal, so we can use this to check if we have found the item.
At this point we see if the we want to use the outIndex pointer from before, if this pointer is not a null pointer we can assume that it is valid and can be filled, so we fill that value with the cursor variable.
Return 1 is returning "true" that that item is definitely in this list.

The final if statement is what shrinks the boundaries by half.
If cursor is the midpoint and we haven't found the item, then + 1 and - 1 would be the new boundaries to search, this cuts the search size by half (Which is why it's called a binary search algorithm), so when we have loads of items in this list, each iteration will cut the search size by half, which helps massively with performance.

Remove

The final function removes the given item from the list (If it is in the list, that is).
Expand to see the code.

You can see the first if statement checks if the list DOES NOT have the variable (Notice the !) and at the same time sets the index variable to the index of the item, this means we don't need to call the function twice, if it returns false then we return from our Remove function, if it returns true then our index variable will have the index of the item. Handy!

The next part uses memmove to shift the rest of the items to overlap the item we are removing. Because this is a sorted list, this action is valid and the list does not need resorting.
memmove is like memcpy, it copies bytes from source pointer into destination pointer, but memmove allows the two pointers to overlap, which is handy when you have lists.

With this use of memmove, if the item we're removing is at the end of the list it will try to copy 0 bytes as ( heapListLen - index - 1 ) will equal zero, so no memory movement actually happens, we then shrink the size of the list with heapListLen--. Notice how we don't resize the pointer with a call to realloc here, realloc is expensive and if memory consumption isn't a concern, you can wait until we use the HeapListAdd() function to realloc the list to the correct size. Alternatively, you can change the Add function so realloc can only grow the list and never shrink the list, this is a good optimisation but can take up a lot of memory if you have thousands of pointers in this list and then delete 99% of them!

The final if statement checks if if the length of the list is now zero, if it is then we can safely free the list's memory and set the list to a null pointer, ready to be reallocated with that realloc function in Add :)

For those who are lazy

At the end of it all, the HeapUtil.h file should look like this:
Expand to see the code.


Changes to Object.c

Since the last post, I changed Object.c to make it more manageable.
Expand to see the code.

I have included the new HeapUtil.h file so the heap management functions can be used.

Object_Init

The change here is that the init function now clears the memory, which will sort out objects on the stack from having garbage data.
The call to Retain is added, it replaces the explicit setting of the retainCount to 1.

Object_Alloc

I replaced the memset with the calloc that I mentioned in the last post, calloc clears the memory that it allocates, it looks like calloc was designed for arrays though as it's first argument is how many items you want to allocate :s

The big change here is that the HeapListAdd() function is used to indicate that Alloc() was called for this object.

Object_Dealloc

A lot has changed here to take advantage of the vtable.
By default, the Dealloc_f vtable entry is zero, which we will interpret as "Use default Object_Dealloc".
The if statement checks if the Dealloc_f has been replaced with a custom function, if it has then it swaps the entry with zero and calls the custom function (At this point, the custom function should call Object_Dealloc() to call the object class' deallocator, which is now set to zero so will do the default behaviour.

In case the custom Dealloc_f function is for some reason not deallocating memory, the table entry is reset to the original value.

When Object_Dealloc is called with the default function, the object is checked to see if it is on the heap, if it is then it frees the memory and removes it from the heap.
If the object is on the stack, the default dealloc function will do nothing.

Retain, Release

These are exactly the same!

Why the changes?

The changes are there to take advantage of the heap management functions so we know what items are on the heap and what are on the stack.
It now means that stack-allocated objects can use the _Dealloc() method, they might have heap-allocated members so it's important to give them a chance to clean up the memory.
The Retain and Release calls now work with stack objects, which is useless as you shouldn't be using stack-declared objects in threads, but if you have a global static class instance then this could be handy.

Trivia time!

I said earlier that these secondary lists are used with garbage collectors, we can actually use this to detect memory leaks too!

Just add a function that will return true if the heapListLen is > 0 and then we know we have a memory leak.
What I do is I assign the Object class with debug names, so when there are memory leaks I can just loop over the heapList and print out the name of the leaked object, if I was really clever I could set up a macro to set the filename and line number of where the object was first allocated!

Also, Ruby and Objective-C use secondary lists for their messaging system. We have used vtables to deal with virtual methods, but Ruby and Obj-C both deal with function calls as strings, they then search their secondary list for the calling object and then match up the string with what ever is in their vtable.
Their vtables are also shared across classes, in our model each class has it's own vtable, so you can see where things start to get complex and slow with Ruby and Obj-C (Linked lists, extra data for functions, storing signatures, etc, etc).

The benefit of these systems is an extremely dynamic calling convention, if you've used either languages you probably dealt with their cool method calling techniques.


Coming up...Our first inheritance!

[Now that the base class is fully working and is all lovely and such, we can create our first USEFUL class!]


Top Top
Profile      
 

    Amy
  Thu Jun 19, 2014 10:27 pm
User avatar
Staff

Big Dumb Guy
Cheers for the info on how to detect memory leaks, I never would have thought of that. I'm pretty sure some of my uni coursework was marred by one.


Top Top
Profile      
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 4 posts ] 


Who is online

Users browsing this forum: No users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

We are an independent, not-for-profit game making community.
Homepage
Board Index
About Us
Downloadable Games
Free Browser Games
Games in Development
RPG Maker Support
Game Maker Support
Construct 2 Support
HBGames the eZine
Advanced RPG Maker
Site Announcements
Powered by phpBB © phpBB Group