#define MLIB__STR(x) #x
#define STR(x) MLIB__STR(x)
-/* --- @GLUE@ --- *
+/* --- @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@.
+ * 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@ --- *
*
# 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: ---
#if GCC_VERSION_P(2, 7) || CLANG_VERSION_P(0, 0)
# define LAUNDER(x) \
({ __typeof__(x) _y; __asm__("" : "=g"(_y) : "0"(x)); _y; })
-# define RELAX do __asm__ __volatile__("" ::: "memory"); while (0)
+# 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 LAUNDER(x) (x)
#endif
+/* --- @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
+
/* --- @RELAX@ --- *
*
* Arguments: ---