X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~mdw/git/mLib/blobdiff_plain/6e683a79101025ee0d371f0b9bece811856edd8d..d04c0e00da3a27693bbf9cc4f2d5c88e56d80f20:/utils/macros.h diff --git a/utils/macros.h b/utils/macros.h index dba22a7..2ca5a06 100644 --- a/utils/macros.h +++ b/utils/macros.h @@ -61,16 +61,18 @@ #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@ --- * * @@ -87,9 +89,72 @@ # 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: --- @@ -197,7 +262,11 @@ #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) @@ -318,6 +387,24 @@ # 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: ---