# include "arena.h"
#endif
+#ifndef MLIB_MACROS_H
+# include "macros.h"
+#endif
+
/*----- Data structures ---------------------------------------------------*/
typedef struct dstr {
* 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@ --- *
*
#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;
# include "dstr.h"
#endif
+#ifndef MLIB_MACROS_H
+# include "macros.h"
+#endif
+
/*----- Magical numbers ---------------------------------------------------*/
#define TEST_FIELDMAX 16 /* Maximum fields in a line */
* 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 -------------------------------------------------*/
#include <stdio.h>
+#ifndef MLIB_MACROS_H
+# include "macros.h"
+#endif
+
/*----- Data structures ---------------------------------------------------*/
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@ --- *
*
extern "C" {
#endif
+/*----- Header files ------------------------------------------------------*/
+
+#ifndef MLIB_MACROS_H
+# include "macros.h"
+#endif
+
/*----- Functions provided ------------------------------------------------*/
/* --- @moan@ --- *
* Use: Reports an error.
*/
-extern void moan(const char *f, ...);
+extern void PRINTF_LIKE(1, 2)
+ moan(const char *f, ...);
/* --- @die@ --- *
*
* 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 -------------------------------------------------*/
.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"
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