Next: , Previous: , Up: Particular Modules   [Contents][Index]

13.4 Compile-time Assertions

This module provides a header file verify.h that defines macros related to compile-time verification.

Two of these macros are 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.

These two macros implement 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.

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.

The verify.h header defines one more macro, assume (E), which expands to an expression of type void that causes the compiler to assume that E yields a nonzero value. E should be a scalar expression, and should not have side effects; it may or may not be evaluated. The behavior is undefined if E would yield zero. The main use of assume is optimization, as the compiler may be able to generate better code if it assumes E. For best results, E should be simple enough that a compiler can determine that it has no side effects: if E calls an external function or accesses volatile storage the compiler may not be able to optimize E away and assume (E) may therefore slow down the program.

Here are some example uses of these macros.

#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))

/* Return T divided by CHAR_MAX + 1, where behavior is
   undefined if T < 0.  In the common case where CHAR_MAX
   is 127 the compiler can therefore implement the division
   by shifting T right 7 bits, an optimization that would
   not be valid if T were negative.  */
time_index (time_t t)
  assume (0 <= t);
  return t / (CHAR_MAX + 1);

Next: , Previous: , Up: Particular Modules   [Contents][Index]