utils/macros.h: Support for compiler-specific annotations.
authorMark Wooding <mdw@distorted.org.uk>
Sat, 22 Jun 2013 13:54:33 +0000 (14:54 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Fri, 28 Jun 2013 22:59:00 +0000 (23:59 +0100)
Add some macros for attaching attributes to functions and so on, for
controlling warnings, and for marking variables as being unused or
ignorable.  Also use them for marking exported functions.

struct/dstr.h
sys/mdup.c
test/testrig.h
trace/trace.h
ui/report.h
utils/macros.3
utils/macros.h

index e1851a2..1c43cb7 100644 (file)
 #  include "arena.h"
 #endif
 
+#ifndef MLIB_MACROS_H
+#  include "macros.h"
+#endif
+
 /*----- Data structures ---------------------------------------------------*/
 
 typedef struct dstr {
@@ -225,7 +229,8 @@ extern int dstr_vputf(dstr */*d*/, const char */*p*/, va_list */*ap*/);
  *             itself is malicious.
  */
 
-extern int dstr_putf(dstr */*d*/, const char */*p*/, ...);
+extern int PRINTF_LIKE(2, 3)
+  dstr_putf(dstr */*d*/, const char */*p*/, ...);
 
 /* --- @dstr_putd@ --- *
  *
index e86f209..ee7816a 100644 (file)
@@ -133,10 +133,12 @@ enum {
 #include <stdarg.h>
 #include <stdio.h>
 
+#include "macros.h"
+
 #define D(x) x
 
-static void dump(mdup_fdinfo *v, size_t n, mdup_fdinfo *dhead,
-                const char *fmt, ...)
+static void PRINTF_LIKE(4, 5) IGNORABLE
+  dump(mdup_fdinfo *v, size_t n, mdup_fdinfo *dhead, const char *fmt, ...)
 {
   int i;
   mdup_fdinfo *f, *g;
index 7a7fdb3..b37aa15 100644 (file)
 #  include "dstr.h"
 #endif
 
+#ifndef MLIB_MACROS_H
+#  include "macros.h"
+#endif
+
 /*----- Magical numbers ---------------------------------------------------*/
 
 #define TEST_FIELDMAX 16               /* Maximum fields in a line */
@@ -115,9 +119,10 @@ extern int test_do(const test_suite /*suite*/[],
  *             working properly.
  */
 
-extern void test_run(int /*argc*/, char */*argv*/[],
-                    const test_chunk /*chunk*/[],
-                    const char */*def*/);
+extern void NORETURN
+  test_run(int /*argc*/, char */*argv*/[],
+          const test_chunk /*chunk*/[],
+          const char */*def*/);
 
 /*----- That's all, folks -------------------------------------------------*/
 
index 77e5ed4..a7edc37 100644 (file)
 
 #include <stdio.h>
 
+#ifndef MLIB_MACROS_H
+#  include "macros.h"
+#endif
+
 /*----- Data structures ---------------------------------------------------*/
 
 typedef struct trace_opt {
@@ -57,7 +61,8 @@ typedef struct trace_opt {
  * Use:                Reports a message to the trace output.
  */
 
-extern void trace(unsigned /*l*/, const char */*f*/, ...);
+extern void PRINTF_LIKE(2, 3)
+  trace(unsigned /*l*/, const char */*f*/, ...);
 
 /* --- @trace_block@ --- *
  *
index 90128fb..4266cc2 100644 (file)
   extern "C" {
 #endif
 
+/*----- Header files ------------------------------------------------------*/
+
+#ifndef MLIB_MACROS_H
+#  include "macros.h"
+#endif
+
 /*----- Functions provided ------------------------------------------------*/
 
 /* --- @moan@ --- *
@@ -44,7 +50,8 @@
  * Use:                Reports an error.
  */
 
-extern void moan(const char *f, ...);
+extern void PRINTF_LIKE(1, 2)
+  moan(const char *f, ...);
 
 /* --- @die@ --- *
  *
@@ -58,7 +65,8 @@ extern void moan(const char *f, ...);
  *             permanent.
  */
 
-extern void die(int status, const char *f, ...);
+extern void PRINTF_LIKE(2, 3) NORETURN
+  die(int status, const char *f, ...);
 
 /*----- That's all, folks -------------------------------------------------*/
 
index 962e81a..bd4145a 100644 (file)
 .SH NAME
 macros \- useful macros
 .\" @N
+.\" @STR
+.\" @GLUE
+.\" @DISCARD
+.\" @IGNORE
+.\" @DEPRECATED
+.\" @EXECL_LIKE
+.\" @IGNORABLE
+.\" @NORETURN
+.\" @PRINTF_LIKE
+.\" @SCANF_LIKE
+.\" @MUFFLE_WARNINGS_DECL
+.\" @MUFFLE_WARNINGS_EXPR
+.\" @MUFFLE_WARNINGS_STMT
+.\" @GCC_VERSION_P
+.\" @GCC_WARNING
 .SH SYNOPSIS
 .nf
 .B "#include <mLib/macros.h>"
 
 .BI "size_t N(" array ");"
+.BI "STR(" tokens\fR... ")"
+.BI "GLUE(" tokens\fR... ", " tokens\fR... ")"
+
+.BI "void DISCARD(" scalar ");"
+.BI "void IGNORE(" variable ");"
+
+.BI "DEPRECATED(" msg ")"
+.BI "EXECL_LIKE(" ntrail ")"
+.BI "IGNORABLE"
+.BI "NORETURN"
+.BI "PRINTF_LIKE(" fmt-index ", " arg-index ")"
+.BI "SCANF_LIKE(" fmt-index ", " arg-index ")"
+
+.BI "MUFFLE_WARNINGS_DECL(" warns ", " decls ")"
+.BI "MUFFLE_WARNINGS_EXPR(" warns ", " expr ")"
+.BI "MUFFLE_WARNINGS_STMT(" warns ", " stmt ")"
+
+.BI "int GCC_VERSION_P(" maj ", " min ");"
+.BI "GCC_WARNING(" option ")"
 .fi
 .SH DESCRIPTION
+.SS Utilities
 The
 .B N
 macro returns the number of elements in the named
 .IR array .
+.PP
+The
+.B STR
+macro expands to a string literal containing the result of expanding its
+argument
+.IR tokens .
+.PP
+The
+.B GLUE
+macro expands to a single token, which is the result of gluing together
+the tokens resulting from expanding its argument token lists.  Each of
+the argument token lists must expand to a single preprocessing token,
+and the result of gluing these tokens together must be valid
+preprocessing token.
+.PP
+The
+.B DISCARD
+macro discards its argument, which must be of some scalar type.  This
+can be useful in muffling warnings about ignoring return codes in cases
+where you really don't care.
+.PP
+The
+.B IGNORE
+macro ignores its argument, which may be an expression of any type.
+This can be useful in muffling warnings about unused variables.
+.SS Annotations
+The following annotations can be attached to function declarations and
+definitions, as part of the declaration specifiers.  (Other positions
+may also work, depending on your compiler, but don't bet on it.)  They
+might not have any effect, depending on your specific compiler.
+Currently only GCC is well supported, but exactly which features are
+available depend on the compiler version.
+.PP
+Using a function or variable marked as
+.B DEPRECATED
+may provoke a compiler warning; this warning may (depending on your
+compiler version) include the given
+.IR msg .
+.PP
+A variadic function marked as
+.B EXECL_LIKE
+must be called with a null pointer (i.e., an integer constant
+expression with value 0, cast to
+.BR "void *")
+in the variadic part of its argument list, followed by
+.I ntrail
+further arguments.  Typically,
+.I ntrail
+is zero.  Compilers may warn about misuse of such functions.
+.PP
+A function or variable marked as
+.B IGNORABLE
+need not be used.  This may muffle warnings about leaving the marked
+definition unused.
+.PP
+A function marked as
+.B NORETURN
+must not return.  It must have return type
+.BR void .
+This may be useful in muffling warnings about uninitialized variables,
+for example.
+.PP
+A variadic function marked as
+.B PRINTF_LIKE
+takes a
+.BR printf (3)-style
+format argument (in position
+.IR fmt-index ,
+counting from 1) and a variable-length list of arguments to be formatted
+(starting from position
+.IR arg-index ).
+Compilers may warn about misuse of such functions.
+.PP
+A variadic function marked as
+.B SCANF_LIKE
+takes a
+.BR scanf (3)-style
+format argument (in position
+.IR fmt-index ,
+counting from 1) and a variable-length list of arguments to be formatted
+(starting from position
+.IR arg-index ).
+Compilers may warn about misuse of such functions.
+.SS Muffling warnings
+Some compilers allow you to muffle warnings in particular pieces of
+code.  These macros provide a compiler-neutral interface to such
+facilities.  Each macro takes an argument
+.IR warns ,
+which is a sequence of calls to
+.IB compiler _WARNING
+macros listing the warnings to be muffled.  The list may contain
+warnings for several different compilers.  The other argument is a
+.I body
+consisting of declarations (in the case of
+.BR MUFFLE_WARNINGS_DECL ),
+an expression (for
+.BR MUFFLE_WARNINGS_EXPR ),
+or a statement (for
+.BR MUFFLE_WARNINGS_STMT ).
+.SS GCC-specific features
+The
+.B GCC_VERSION_P
+macro returns a nonzero value if the compiler is at least version
+.IB maj . min
+of GCC, and zero otherwise.  It's useful in preprocessor conditions.
+.PP
+The
+.B GCC_WARNING
+macro is intended to be used in
+.I warns
+lists (see above).  It takes a string-literal argument
+.I option
+naming a GCC warning option, e.g.,
+.BR """\-Wdiv-by-zero""" .
 .SH "SEE ALSO"
 .BR mLib (3).
 .SH "AUTHOR"
index 41f2d67..b960475 100644 (file)
@@ -32,7 +32,7 @@
   extern "C" {
 #endif
 
-/*----- Macros provided ---------------------------------------------------*/
+/*----- Miscellaneous utility macros --------------------------------------*/
 
 #define N(v) (sizeof(v)/sizeof(*v))
 
 #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