One standard header file that I end up replacing completely is assert.h. There are several reasons:
assert() macro needs to be rewritten so that additional actions happen on failure.
assert() macro are desirable, including bounds().
assert() by defining NDEBUG to 1/0 instead of by undefining/defining it.
For instance, assume that the standard definition of assert() is:
#define assert(e) ((e)?(void)0:__assert(__FILE__,__LINE__, #e))
In that case, I end up defining proj_assert.h as follows:
#ifndef H_proj_assert
#define H_proj_assert
extern void __assert( const char *, int, const char *);
#if NDEBUG
#define assert(_e) ((void)0)
#define massert(_e,_m) ((void)0)
#define bounds(_i,_n) ((void)0)
#else
/* standard assert macro */
#define assert(_e) \
((_e)?(void)0: \
stacktrace_print(), \
__assert(__FILE__, __LINE__, #_e))
/* an assert variant that prints out a user supplied message */
#define massert(_e, _m) \
((_e)?(void)0:
stacktrace_print(), \
__assert(__FILE__, __LINE__, _m))
/* an assert variant that checks for array-out-of-bounds */
#define bounds(_i, _n) \
(((unsigned)(_i)<(_n))? (_i) : \
( stacktrace_print(), \
__assert(__FILE__, __LINE__, \
"index '" #_i "' out of bounds '" #_n "'"), 0))
#endif
#endif
Note that we alter the assert() macros to print the stack trace, as discussed previously prior to exiting. Further, we define other assert() macros. One in particular, massert() is new.
Consider the case of using assert() to verify that we are not popping from an empty stack. The check is assert(len_int_stack(stack) > 0). If this fails, the error message will be something like:
assert "len_int_stack(stack) > 0" failed: file "int_stack.c", line 100
This tells us what happened, but does not adequately describe the problem. Instead, we can write massert(len_int_stack(stack) > 0, "popping empty stack"). In this case the error message will be:
assert "popping empty stack" failed: file "int_stack.c", line 100