X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~mdw/git/mLib/blobdiff_plain/33e3ac9056f046affd69e3b8871275e5e2697aa4..08bb7015a9e28c5c9d38fe05a6f6644bc21fa527:/utils/macros.h diff --git a/utils/macros.h b/utils/macros.h index bcceecd..2ca5a06 100644 --- a/utils/macros.h +++ b/utils/macros.h @@ -34,39 +34,239 @@ /*----- Header files ------------------------------------------------------*/ +#include + #ifndef MLIB_COMPILER_H # include "compiler.h" #endif /*----- Miscellaneous utility macros --------------------------------------*/ -#define N(v) (sizeof(v)/sizeof(*v)) +/* --- @N@ --- * + * + * Arguments: @type v[]@ = an actual array, not a pointer + * + * Returns: The number of elements in @v@. + */ + +#define N(v) (sizeof(v)/sizeof(*(v))) + +/* --- @STR@ --- * + * + * Arguments: @x@ = some tokens + * + * Returns: A string literal containing the macro-expanded text of @x@. + */ #define MLIB__STR(x) #x #define STR(x) MLIB__STR(x) +/* --- @GLUE@, @GLUE3@ --- * + * + * Arguments: @x, y@ = two sequences of tokens + * @z@ = a third sequence of tokens + * + * Returns: A single token formed by gluing together the macro-expansions + * of @x@ and @y@, and @z@ for @GLUE3@. + */ + #define MLIB__GLUE(x, y) x##y #define GLUE(x, y) MLIB__GLUE(x, y) +#define GLUE3(x, y, z) GLUE(x, MLIB__GLUE(y, z)) + +/* --- @STATIC_ASSERT@ --- * + * + * Arguments: @int cond@ = a condition + * @msg@ = a string literal message + * + * Returns: --- + * + * Use: Fail at compile time unless @cond@ is nonzero. The failure + * might report @msg@. + */ + +#ifdef static_assert +# define STATIC_ASSERT(cond, msg) static_assert(!!(cond), msg) +#else +# define STATIC_ASSERT(cond, msg) \ + IGNORABLE extern char static_assert_failed[1 - 2*!(cond)] +#endif + +/* --- @CHECK_TYPE@ --- + * + * Arguments: @expty@ = expected type of @x@ + * @expty x@ = some object + * + * Returns: Integer zero. + * + * Use: Cause a compile-time failure unless the type of @x@ is + * assignment-compatible with @expty@. + */ + +#define CHECK_TYPE(expty, x) (!sizeof(*(expty *)0 = (x))) + +/* --- @CONVERT_CAREFULLY@ --- + * + * Arguments: @newty@ = new type for the result + * @expty@ = expected type of @x@ + * @expty x@ = some object + * + * Returns: @x@, but coerced to type @newty@. + * + * Use: Like @(newty)x@, except that it checks at compile-time that + * @x@ is at least assignment-compatible with type @expty@ + * before trying. + */ + +#define CONVERT_CAREFULLY(newty, expty, x) \ + (CHECK_TYPE(expty, x) + (/*unconst unvolatile*/ newty)(x)) + +/* --- @UNCONST@, @UNVOLATILE@, @UNQUALIFY@ --- * + * + * Arguments: @type@ = a type name + * @type *p@ = a pointer + * + * Returns: @p@, but without @const@, @volatile@ or both qualifiers. + * + * Use: Strips qualifiers from pointer types. + * + * The @UNCONST@ macro strips @const@. It checks that @p@ + * has type `pointer to @type@ or @const type@'; if not, a + * compile-time error results. Otherwise, it returns the value + * of @p@, converted to `pointer to (non-constant) @type@'. It + * will not silently strip a @volatile@ qualifier. + * + * The @UNVOLATILE@ macro is similar, except that it strips + * @volatile@ instead of @const@. The @UNQUALIFY@ macro strips + * both qualifiers. + */ + +#define UNCONST(type, p) CONVERT_CAREFULLY(type *, const type *, p) +#define UNVOLATILE(type, p) CONVERT_CAREFULLY(type *, volatile type *, p) +#define UNQUALIFY(type, p) \ + CONVERT_CAREFULLY(type *, const volatile type *, p) + +/* --- @EMPTY@ --- * + * + * Arguments: --- + * + * Returns: The empty token sequence. + */ + +#define EMPTY + +/* --- @COMMA@ --- * + * + * Arguments: --- + * + * Returns: A `%|,|%' token, which can be usefully passed to macros to + * avoid argument splitting. + */ + +#define COMMA , + +/*----- String and character hacks ----------------------------------------*/ + +/* --- @IS...@ --- * + * + * Arguments: @int ch@ = a character code, but not @EOF@ + * + * Returns: Nonzero if @ch@ is in the relevant @@ category. + * + * Use: Classifies characters, but safely even if characters are + * signed. + * + * There is a macro for each of the @@ @is...@ + * functions. + */ -/*----- Compiler diagnostics ----------------------------------------------*/ +#define CTYPE_HACK(func, ch) (func((unsigned char)(ch))) + +#define ISALNUM(ch) CTYPE_HACK(isalnum, ch) +#define ISALPHA(ch) CTYPE_HACK(isalpha, ch) +#define ISASCII(ch) CTYPE_HACK(isascii, ch) +#define ISBLANK(ch) CTYPE_HACK(isblank, ch) +#define ISCNTRL(ch) CTYPE_HACK(iscntrl, ch) +#define ISDIGIT(ch) CTYPE_HACK(isdigit, ch) +#define ISGRAPH(ch) CTYPE_HACK(isgraph, ch) +#define ISLOWER(ch) CTYPE_HACK(islower, ch) +#define ISPRINT(ch) CTYPE_HACK(isprint, ch) +#define ISPUNCT(ch) CTYPE_HACK(ispunct, ch) +#define ISSPACE(ch) CTYPE_HACK(isspace, ch) +#define ISUPPER(ch) CTYPE_HACK(isupper, ch) +#define ISXDIGIT(ch) CTYPE_HACK(isxdigit, ch) + +/* --- @TO...@ --- * + * + * Arguments: @int ch@ = a character code, but not @EOF@ + * + * Returns: The converted character code. + * + * Use: Converts characters, but safely even if characters are + * signed. + * + * There is a macro for each of the @@ @to...@ + * functions. + */ -/* --- Compiler-specific definitions --- */ +#define TOASCII(ch) CTYPE_HACK(toascii, ch) +#define TOLOWER(ch) CTYPE_HACK(tolower, ch) +#define TOUPPER(ch) CTYPE_HACK(toupper, ch) + +/* --- @MEMCMP@, @STRCMP@, @STRNCMP@ --- * + * + * Arguments: @const type *x, *y@ = pointers to strings + * @op@ = a relational operator symbol + * @size_t n@ = length of the strings + * + * Returns: Nonzero if the relationship between the strings satisfies the + * operator @op@, otherwise zero. + * + * Use: These macros mitigate the author's frequent error of failing + * to compare the result of the underlying standard functions + * against zero, effectively reversing the sense of an intended + * test for equality. + */ + +#define MEMCMP(x, op, y, n) (memcmp((x), (y), (n)) op 0) +#define STRCMP(x, op, y) (strcmp((x), (y)) op 0) +#define STRNCMP(x, op, y, n) (strncmp((x), (y), (n)) op 0) + +/*----- Compiler-specific definitions -------------------------------------*/ + +/* The descriptions of these are given below, with the fallback + * definitions. + */ #if GCC_VERSION_P(2, 5) || CLANG_VERSION_P(3, 3) -# define NORETURN __attribute__((noreturn)) -# define PRINTF_LIKE(fix, aix) __attribute__((format(printf, fix, aix))) -# define SCANF_LIKE(fix, aix) __attribute__((format(scanf, fix, aix))) -# define IGNORABLE __attribute__((unused)) +# define NORETURN __attribute__((__noreturn__)) +# define PRINTF_LIKE(fix, aix) __attribute__((__format__(printf, fix, aix))) +# define SCANF_LIKE(fix, aix) __attribute__((__format__(scanf, fix, aix))) +# define IGNORABLE __attribute__((__unused__)) +#endif + +#if GCC_VERSION_P(3, 4) || CLANG_VERSION_P(3, 3) +# define MUST_CHECK __attribute__((__warn_unused_result__)) #endif #if GCC_VERSION_P(4, 5) || CLANG_VERSION_P(3, 3) -# define DEPRECATED(msg) __attribute__((deprecated(msg))) +# define DEPRECATED(msg) __attribute__((__deprecated__(msg))) #elif GCC_VERSION_P(3, 1) -# define DEPRECATED(msg) __attribute__((deprecated)) +# define DEPRECATED(msg) __attribute__((__deprecated__)) #endif #if GCC_VERSION_P(4, 0) || CLANG_VERSION_P(3, 3) -# define EXECL_LIKE(ntrail) __attribute__((sentinel(ntrail))) +# define EXECL_LIKE(ntrail) __attribute__((__sentinel__(ntrail))) +#endif + +#if GCC_VERSION_P(2, 7) || CLANG_VERSION_P(0, 0) +# define LAUNDER(x) \ + ({ __typeof__(x) _y; __asm__("" : "=g"(_y) : "0"(x)); _y; }) +# define ADMIRE(x) \ + ({ __asm__("" :: "g"(x)); }) +# define ADMIRE_BUF(p, sz) \ + ({ __asm__("" :: "m"(*(unsigned char *)p), "g"(sz) : "memory"); }) +# define RELAX do __asm__(""); while (0) #endif #if CLANG_VERSION_P(3, 3) @@ -86,7 +286,9 @@ # define MUFFLE_WARNINGS_STMT(warns, body) \ do { MLIB__MUFFLE_WARNINGS(warns, body) } while (0) -#elif GCC_VERSION_P(4, 6) +#endif + +#if GCC_VERSION_P(4, 6) /* --- Diagnostic suppression in GCC: a tale of woe --- * * @@ -144,34 +346,141 @@ /* --- Fallback definitions, mostly trivial --- */ -#ifndef DEPRECATED -# define DEPRECATED(msg) -#endif - -#ifndef EXECL_LIKE -# define EXECL_LIKE(ntrail) -#endif +/* --- @DISCARD@ --- * + * + * Arguments: @x@ = a function call + * + * Returns: --- + * + * Use: Explicitly discard the result of @x@. This counteracts a + * @MUST_CHECK@ attribute on the called function. + */ #ifndef DISCARD # define DISCARD(x) do if (x); while (0) #endif +/* --- @IGNORE@ --- * + * + * Arguments: @x@ = any expression + * + * Returns: --- + * + * Use: Ignore the value of @x@, overriding compiler warnings. + */ + #ifndef IGNORE # define IGNORE(x) ((void)(x)) #endif -#ifndef MUFFLE_WARNINGS_DECL -# define MUFFLE_WARNINGS_DECL(warns, body) body +/* --- @LAUNDER@ --- * + * + * Arguments: @x@ = some integer expression + * + * Returns: @x@. + * + * Use: Causes a compiler to know nothing about the value of @x@, + * even if it looks obvious, e.g., it's a constant. + */ + +#ifndef LAUNDER +# define LAUNDER(x) (x) #endif -#ifndef MUFFLE_WARNINGS_EXPR -# define MUFFLE_WARNINGS_EXPR(warns, body) (body) +/* --- @ADMIRE@, @ADMIRE_BUF@ --- * + * + * Arguments: @x@ = some scalar expression + * @const void *p@, @size_t sz@ = a pointer and length + * + * Returns: --- + * + * Use: Ensures that the compiler generates code to compute @x@ or + * the contents of the buffer at @p@. + */ + +#ifndef ADMIRE +# define ADMIRE(x) ((void)(x)) +#endif +#ifndef ADMIRE_BUF +# define ADMIRE_BUF(p, sz) ((void)(p), (void)(sz)) #endif -#ifndef MUFFLE_WARNINGS_STMT -# define MUFFLE_WARNINGS_STMT(warns, body) do { body } while (0) +/* --- @RELAX@ --- * + * + * Arguments: --- + * + * Returns: --- + * + * Use: Does nothing, but the compiler doesn't know that. + */ + +#ifndef RELAX +# define RELAX +#endif + +/* --- @DEPRECATED@, @NORETURN@, @IGNORABLE@, @MUST_CHECK@ --- * + * + * Use: These are (mostly) function attributes; write them among the + * declaration specifiers for a function definition or + * declaration. These may not do anything, but the intended + * behaviour is as follows. + * + * * @DEPRECATED(msg)@ -- report a warning, quoting the string + * literal @msg@, if the function is called. + * + * * @NORETURN@ -- promise that the function doesn't return to + * its caller: either it kills the process, or it performs + * some nonlocal transfer. + * + * * @IGNORABLE@ -- the item (which might be data rather than + * a function) might not be referred to, but that's OK: + * don't warn about it. + * + * @ @MUST_CHECK@ -- warn if the return value of a function is + * ignored. Use @DISCARD@ if you really don't care. + */ + +#ifndef DEPRECATED +# define DEPRECATED(msg) +#endif + +#ifndef NORETURN +# define NORETURN +#endif + +#ifndef IGNORABLE +# define IGNORABLE #endif +#ifndef MUST_CHECK +# define MUST_CHECK +#endif + +/* --- @PRINTF_LIKE@, @SCANF_LIKE@, @EXECL_LIKE@ --- * + * + * Arguments: @int fmtix@ = format string argument index (starting from 1) + * @int argix@ = variable format argument tail index (starting + * from 1) + * @int ntrail@ = number of arguments following terminator + * + * Use: These are function attributes. Again, they might not do + * anything at all. By intention, they give the compiler + * information about a variadic function's arguments, so that it + * can warn about misuse. + * + * * @PRINTF_LIKE@ -- the function takes a @printf@-style + * format string as argument @fmtix@ and an argument tail + * (which may be empty) beginning with argument @argix@. + * + * * @SCANF_LIKE@ -- the function takes a @scanf@-style + * format string as argument @fmtix@ and an argument tail + * (which may be empty) beginning with argument @argix@. + * + * * @EXECL_LIKE@ -- the function takes a sequence of pointer + * arguments terminated by a null pointer, followed by + * @ntrail@ further arguments. + */ + #ifndef PRINTF_LIKE # define PRINF_LIKE(fmtix, argix) #endif @@ -180,14 +489,61 @@ # define SCANF_LIKE(fmtix, argix) #endif -#ifndef IGNORABLE -# define IGNORABLE +#ifndef EXECL_LIKE +# define EXECL_LIKE(ntrail) #endif +/* --- @MUFFLE_WARNINGS_...@ --- * + * + * Arguments: @warns@ = a sequence of @..._WARNING@ calls (see below) + * @body@ = some program text + * + * Use: Muffle specific warnings within the program text. + * + * For @MUFFLE_WARNINGS_DECL@, the program text is a + * declaration; for @MUFFLE_WARNINGS_EXPR@, it is an expression, + * and for @MUFFLE_WARNINGS_STMT@, it is a statement. + * + * The warnings to be muffled are given as a list of + * @..._WARNING@ macros, with no separators. The list can + * list warnings from multiple different compilers: entries for + * irrelevant compilers will be ignored. + */ + +#ifndef MUFFLE_WARNINGS_DECL +# define MUFFLE_WARNINGS_DECL(warns, body) body +#endif + +#ifndef MUFFLE_WARNINGS_EXPR +# define MUFFLE_WARNINGS_EXPR(warns, body) (body) +#endif + +#ifndef MUFFLE_WARNINGS_STMT +# define MUFFLE_WARNINGS_STMT(warns, body) do { body } while (0) +#endif + +/* --- @GCC_WARNING@ --- * + * + * Arguments: @warn@ = a string literal naming a warning, with `%|-W...|%' + * prefix + * + * Use: Names a GCC warning: use within @MUFFLE_WARNINGS_...@. + * + * Note that GCC's warning suppression is very buggy. + */ + #ifndef GCC_WARNING # define GCC_WARNING(warn) #endif +/* --- @CLANG_WARNING@ --- * + * + * Arguments: @warn@ = a string literal naming a warning, with `%|-W...|%' + * prefix + * + * Use: Names a Clang warning: use within @MUFFLE_WARNINGS_...@. + */ + #ifndef CLANG_WARNING # define CLANG_WARNING(warn) #endif