Pointer handling in C is very dangerous and error prone. A heap wrapper helps to control this dangers. heap.h and heap.c offer the following memory handling functionality:
For this purpose various macros are defined. The most important are HEAP_ALLOC(), HEAP_FREE() and HEAP_CHECKPTR(). Have a look at heap.h for a detailed description of the available macros. The functionality of heap.c is only available while debugging. If the NDEBUG macro is defined it simply uses normal calloc() and free(). Here is how car.c is implemented by using a heap wrapper.
car.c
#include "../obc/heap.h"
#include "../obc/objI.h"
#include "car.h"
#include "vehicleI.h"
typedef struct CarImpl {
float speed;
} CarImpl;
#define testCar(self) HEAP_CHECKPTR(self, CarImpl);
void car_print(Car self, FILE* out) {
testCar(self);
fprintf(out, "Car speeds at %.0f km/h\n", self->speed);
}
void car_accelerate(Car self) {
testCar(self);
self->speed += 10;
}
float car_getSpeed(Car self) {
testCar(self);
return self->speed;
}
void car_destroy(Car self) {
testCar(self);
obj_destroyInfo(self);
HEAP_FREE(self);
}
Car car_create() {
Car self = HEAP_ALLOC(CarImpl, 1);
REGISTER(self, obj_print, car_print);
REGISTER(self, obj_destroy, car_destroy);
REGISTER(self, obj_accelerate, car_accelerate);
REGISTER(self, obj_getSpeed, car_getSpeed);
self->speed = 0.0f;
return self;
}
With HEAP_CHECKPTR() the macro testCar() is defined. It is used to ensure that
each self is a valid car memory. If you check all your self pointers like
this, this will save you from a lot of troubles :-)
When you compile with NDEBUG defined, all the checks are turned of as with
normal asserts.