In the previous two sections, we defined two concepts that allow us to gain confidence that:
Going back to our int_stack
example, we can add to our error checking as follows:
void
verify_int_stack(
const char * file,
int line,
int_stack stack
)
{
verify( file, line, is_heap_ptr(stack));
verify( file, line, is_heap_ptr(vals_int_stack(stack)));
verify( file, line, xtype_ptr(struct int_stack, stack) );
verify( file, line,
xntype_ptr(int, max_int_stack(stack), vals_int_stack(stack)));
/* other verification ... */
}
In the worst case, we can always check every access to the int stack data structure. In this case we make use of the deref_int_stack()
macro.
#define deref_int_stack(_s) \
( assert(is_heap_ptr(_s)), \
assert(xtype_ptr(struct int_stack, _s)), \
(_s) )
Another obvious place to use these macros is just prior to freeing.
#define xfree(_t,_p) \
( assert(is_heap_ptr(_p)), \
assert(xtype_ptr(_t, _p)), \
free(_p) )
#define xnfree(_t, _n, _p) \
( assert(is_heap_ptr(_p)), \
assert(xntype_ptr(_t, _n, _p)), \
free(_p) )