Next: , Previous: Safe Allocation Macros, Up: Particular Modules


13.4 Compile-time Assertions

The ‘verify’ module supports compile-time tests, as opposed to the standard assert macro which supports only runtime tests. Since the tests occur at compile-time, they are more reliable, and they require no runtime overhead.

This module provides a header file verify.h that defines two macros: verify (V) and verify_expr (V, EXPR). Both accept an integer constant expression argument V and verify that it is nonzero. If not, a compile-time error results.

verify (V); is a declaration; it can occur outside of functions. In contrast, verify_expr (V, EXPR) is an expression that returns the value of EXPR; it can be used in macros that expand to expressions. If EXPR is an integer constant expression, then verify_expr (V, EXPR) is also an integer constant expression. Although EXPR and verify_expr (V, EXPR) are guaranteed to have the same side effects and value and type (after integer promotion), they need not have the same type if EXPR's type is an integer that is narrower than int or unsigned int.

V should be an integer constant expression in the sense of the C standard. Its leaf operands should be integer, enumeration, or character constants; or sizeof expressions that return constants; or floating constants that are the immediate operands of casts. Outside a sizeof subexpression, V should not contain any assignments, function calls, comma operators, casts to non-integer types, or subexpressions whose values are outside the representable ranges for their types. If V is not an integer constant expression, then a compiler might reject a usage like ‘verify (V);’ even when V is nonzero.

Although the standard assert macro is a runtime test, C11 specifies a builtin _Static_assert (V, STRING-LITERAL), its assert.h header has a similar macro named static_assert, and C++11 has a similar static_assert builtin. These builtins and macros differ from verify in two major ways. First, they can also be used within a struct or union specifier, in place of an ordinary member declaration. Second, they require the programmer to specify a compile-time diagnostic as a string literal.

Here are some example uses of verify and verify_expr.

     #include <verify.h>
     
     #include <limits.h>
     #include <time.h>
     
     /* Verify that time_t is an integer type.  */
     verify ((time_t) 1.5 == 1);
     
     /* Verify that time_t is no smaller than int.  */
     verify (sizeof (int) <= sizeof (time_t));
     
     /* Verify that time_t is signed.  */
     verify ((time_t) -1 < 0);
     
     /* Verify that time_t uses two's complement representation.  */
     verify (~ (time_t) -1 == 0);
     
     /* Return the maximum value of the integer type T,
        verifying that T is an unsigned integer type.
        The cast to (T) is outside the call to verify_expr
        so that the result is of type T
        even when T is narrower than unsigned int.  */
     #define MAX_UNSIGNED_VAL(t) \
        ((T) verify_expr (0 < (T) -1, -1))