X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~mdw/git/mLib/blobdiff_plain/e6591bec0938b76b8c02083eb6ba101aba4e1b7e..08bb7015a9e28c5c9d38fe05a6f6644bc21fa527:/utils/macros.h diff --git a/utils/macros.h b/utils/macros.h index 73230bd..2ca5a06 100644 --- a/utils/macros.h +++ b/utils/macros.h @@ -42,25 +42,144 @@ /*----- Miscellaneous utility macros --------------------------------------*/ +/* --- @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[2*!!(cond) - 1] + 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. + */ + #define CTYPE_HACK(func, ch) (func((unsigned char)(ch))) #define ISALNUM(ch) CTYPE_HACK(isalnum, ch) @@ -77,17 +196,47 @@ #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. + */ + #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 diagnostics ----------------------------------------------*/ +/*----- Compiler-specific definitions -------------------------------------*/ -/* --- 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__)) @@ -110,6 +259,16 @@ # 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) # define MLIB__PRAGMA_HACK(x) _Pragma(#x) @@ -127,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 --- * * @@ -185,40 +346,102 @@ /* --- 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 -#endif +/* --- @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 MUFFLE_WARNINGS_EXPR -# define MUFFLE_WARNINGS_EXPR(warns, body) (body) +#ifndef LAUNDER +# define LAUNDER(x) (x) #endif -#ifndef MUFFLE_WARNINGS_STMT -# define MUFFLE_WARNINGS_STMT(warns, body) do { body } while (0) +/* --- @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 PRINTF_LIKE -# define PRINF_LIKE(fmtix, argix) +/* --- @RELAX@ --- * + * + * Arguments: --- + * + * Returns: --- + * + * Use: Does nothing, but the compiler doesn't know that. + */ + +#ifndef RELAX +# define RELAX #endif -#ifndef SCANF_LIKE -# define SCANF_LIKE(fmtix, argix) +/* --- @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 @@ -233,10 +456,94 @@ # 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 + +#ifndef SCANF_LIKE +# define SCANF_LIKE(fmtix, argix) +#endif + +#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