Sometimes an insertion or deletion must succeed because it is known in advance that there is no way that it can fail. For instance, we might be inserting into a table from a list of items known to be unique, using a memory allocator that cannot return a null pointer. In this case, we want to make sure that the operation succeeded, and abort if not, because that indicates a program bug. We also would like to be able to turn off these tests for success in our production versions, because we don't want them slowing down the code.
11. <Table assertion function prototypes 11> = void tbl_assert_insert (struct tbl_table *, void *); void *tbl_assert_delete (struct tbl_table *, void *);
This code is included in 15.
These functions provide assertions for tbl_insert() and tbl_delete(). They expand, via macros, directly into calls to those functions when NDEBUG, the same symbol used to turn off assert() checks, is declared. As for the standard C header <assert.h>, header files for tables may be included multiple times in order to turn these assertions on or off.
1. Write a set of preprocessor directives for a table header file that implement the behavior described in the final paragraph above. [answer]
2. Write a generic implementation of tbl_assert_insert() and tbl_assert_delete() in terms of existing table functions. Consider the base functions carefully. Why must we make sure that assertions are always enabled for these functions? [answer]
3. Why must tbl_assert_insert() not be used if the table's memory
allocator can fail? (See also Exercise 2.8-1.)