X-Git-Url: https://git.distorted.org.uk/~mdw/mLib/blobdiff_plain/c4ccbbf992e1587ae316f66af6a28006780688d8..6e683a79101025ee0d371f0b9bece811856edd8d:/utils/macros.h diff --git a/utils/macros.h b/utils/macros.h index 95d0ddb..dba22a7 100644 --- a/utils/macros.h +++ b/utils/macros.h @@ -42,14 +42,47 @@ /*----- 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@ --- * + * + * Arguments: @x, y@ = two sequences of tokens + * + * Returns: A single token formed by gluing together the macro-expansions + * of @x@ and @y@. + */ + #define MLIB__GLUE(x, y) x##y #define GLUE(x, y) MLIB__GLUE(x, y) +/* --- @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 @@ -57,10 +90,31 @@ IGNORABLE extern char static_assert_failed[2*!!(cond) - 1] #endif +/* --- @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 +131,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__)) @@ -193,40 +277,84 @@ /* --- 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) -#endif +/* --- @RELAX@ --- * + * + * Arguments: --- + * + * Returns: --- + * + * Use: Does nothing, but the compiler doesn't know that. + */ -#ifndef PRINTF_LIKE -# define PRINF_LIKE(fmtix, argix) +#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 @@ -241,18 +369,94 @@ # define MUST_CHECK #endif -#ifndef LAUNDER -# define LAUNDER +/* --- @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 RELAX -# define RELAX +#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