X-Git-Url: https://git.distorted.org.uk/~mdw/mLib/blobdiff_plain/5b078c11a05739c5869a6fff858019a7c520ca83..3832000d8a0d417dbfda0ca2d26bc7d66f7f4741:/utils/macros.h diff --git a/utils/macros.h b/utils/macros.h index 41f2d67..b960475 100644 --- a/utils/macros.h +++ b/utils/macros.h @@ -32,7 +32,7 @@ extern "C" { #endif -/*----- Macros provided ---------------------------------------------------*/ +/*----- Miscellaneous utility macros --------------------------------------*/ #define N(v) (sizeof(v)/sizeof(*v)) @@ -42,6 +42,140 @@ #define MLIB__GLUE(x, y) x##y #define GLUE(x, y) MLIB__GLUE(x, y) +/*----- Compiler diagnostics ----------------------------------------------*/ + +/* --- Compiler-specific definitions --- */ + +#if defined(__GNUC__) + +# define GCC_VERSION_P(maj, min) \ + (__GNUC__ > (maj) || (__GNUC__ == (maj) && __GNUC_MINOR__ >= (min))) + +# if GCC_VERSION_P(2, 5) +# define NORETURN __attribute__((noreturn)) +# define PRINTF_LIKE(fix, aix) __attribute__((format(printf, fix, aix))) +# define SCANF_LIKE(fix, aix) __attribute__((format(scanf, fix, aix))) +# define IGNORABLE __attribute__((unused)) +# endif + +# if GCC_VERSION_P(4, 5) +# define DEPRECATED(msg) __attribute__((deprecated(msg))) +# elif GCC_VERSION_P(3, 1) +# define DEPRECATED(msg) __attribute__((deprecated)) +# endif + +# if GCC_VERSION_P(4, 0) +# define EXECL_LIKE(ntrail) __attribute__((sentinel(ntrail))) +# endif + +# if GCC_VERSION_P(4, 6) + + /* --- Diagnostic suppression in GCC: a tale of woe --- * + * + * This is extremely unpleasant, largely as a result of bugs in the GCC + * preprocessor's handling of @_Pragma@. The fundamental problem is + * that it's the preprocessor, and not the compiler proper, which + * detects @_Pragma@, emitting @#pragma@ lines into its output; and it + * does it during macro expansion, even if the macro is being expanded + * during argument collection. Since arguments are expanded before + * replacing the macro's invocation with its body, a pragma in an + * argument will be emitted %%\emph{before}%% any pragmata in the body, + * even if they appear before the argument in the body -- and even if + * the argument doesn't actually appear anywhere at all in the body. + * + * Another, rather less significant, problem is that @_Pragma@'s + * argument is a single string literal, recognized in translation phase + * 4, before string-literal concatenation in phase 6, so we must build + * pragma bodies as token lists and then stringify them. + * + * As a result, we need some subterfuge here. The @MLIB__PRAGMA_HACK@ + * macro issues a @_Pragma@ on its argument token list, which it + * stringifies; this deals with the second problem. The first is + * trickier: we must delay expansion of @MLIB__PRAGMA_HACK@ from the + * argument collection phase to the body rescanning phase, and we do + * this by splitting the invocations between @GCC_WARNING@ macro calls: + * the name is left hanging from the previous call (or from + * @MLIB__MUFFLE_WARNINGS@, in the first case) and the body is supplied + * by @GCC_WARNING@, which also supplies the next @MLIB__PRAGMA_HACK@. + * The remaining problem is to make sure we can dispose of the final + * trailing @MLIB__PRAGMA_HACK@ harmlessly, which we do by introducing + * an extra argument @emitp@, which may be either @t@ or @nil@; this + * dispatches to an appropriate helper macro by means of token-pasting. + * + * I'm so sorry. + */ + +# define MLIB__PRAGMA_HACK_t(x) _Pragma(#x) +# define MLIB__PRAGMA_HACK_nil(x) +# define MLIB__PRAGMA_HACK(emitp, x) MLIB__PRAGMA_HACK_##emitp(x) +# define MLIB__MUFFLE_WARNINGS(warns, body) \ + _Pragma("GCC diagnostic push") MLIB__PRAGMA_HACK \ + warns \ + (nil, nil) \ + body \ + _Pragma("GCC diagnostic pop") +# define GCC_WARNING(warn) \ + (t, GCC diagnostic ignored warn) MLIB__PRAGMA_HACK +# define MUFFLE_WARNINGS_DECL(warns, body) \ + MLIB__MUFFLE_WARNINGS(warns, body) +# define MUFFLE_WARNINGS_EXPR(warns, body) \ + __extension__ ({ MLIB__MUFFLE_WARNINGS(warns, (body);) }) +# define MUFFLE_WARNINGS_STMT(warns, body) \ + do { MLIB__MUFFLE_WARNINGS(warns, body) } while (0) +# endif + +#endif + +/* --- Fallback definitions, mostly trivial --- */ + +#ifndef GCC_VERSION_P +# define GCC_VERSION_P(maj, min) 0 +#endif + +#ifndef DEPRECATED +# define DEPRECATED(msg) +#endif + +#ifndef EXECL_LIKE +# define EXECL_LIKE(ntrail) +#endif + +#ifndef DISCARD +# define DISCARD(x) do if (x); while (0) +#endif + +#ifndef IGNORE +# define IGNORE(x) ((void)(x)) +#endif + +#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 + +#ifndef PRINTF_LIKE +# define PRINF_LIKE(fmtix, argix) +#endif + +#ifndef SCANF_LIKE +# define SCANF_LIKE(fmtix, argix) +#endif + +#ifndef IGNORABLE +# define IGNORABLE +#endif + +#ifndef GCC_WARNING +# define GCC_WARNING(warn) +#endif + /*----- That's all, folks -------------------------------------------------*/ #ifdef __cplusplus