/*----- 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 @<ctype.h>@ category.
+ *
+ * Use: Classifies characters, but safely even if characters are
+ * signed.
+ *
+ * There is a macro for each of the @<ctype.h>@ @is...@
+ * functions.
+ */
+
#define CTYPE_HACK(func, ch) (func((unsigned char)(ch)))
#define ISALNUM(ch) CTYPE_HACK(isalnum, 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 @<ctype.h>@ @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__))
# 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)
# 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 --- *
*
/* --- 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
# 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