The module ‘verify’ 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, C23 and C++17
specify a builtin static_assert (V)
, which differs
from verify
in two major ways. First, it can also be used
within a struct
or union
specifier, in place of an
ordinary member declaration. Second, it allows the programmer to
specify, as an optional second argument, a compile-time diagnostic as
a string literal. If your program is not intended to be portable to
compilers that lack C23 or C++17 static_assert
, the only
advantage of verify
is that its name is a bit shorter.
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_t time_index (time_t t) { assume (0 <= t); return t / (CHAR_MAX + 1); }