following up on bug 19239 - verbose deprecation warnings .vs. the C preprocessor
Zack Weinberg
zackw@panix.com
Wed Sep 28 14:38:00 GMT 2016
I thought I would look again at _Pragma("GCC warning") for the
deprecation messages for major/minor/makedev. As a reminder, this is
a minimal C++ scenario where we want to print the deprecation message
(see https://sourceware.org/bugzilla/show_bug.cgi?id=19239):
#include <sys/types.h>
struct A {
unsigned major;
A (unsigned major) : major (major) {}
};
With g++ 6 and glibc 2.24 this gives a confusing message:
In file included from /usr/include/x86_64-linux-gnu/sys/types.h:222:0,
from test.cc:1:
test.cc: In constructor ‘A::A(unsigned int)’:
test.cc:4:24: error: class ‘A’ does not have any field named ‘gnu_dev_major’
A (unsigned major) : major (major) {}
^
glibc 2.25 *intends* to print a more helpful message, but it doesn't
happen, because we're using `__attribute__ ((deprecated ("...")))` to
do it and this construct does not count as calling a deprecated
function. If we could use `_Pragma ("GCC warning \"...\"")` instead,
that would work better.
The problem with that idea is, the raw form of the pragma,
#pragma GCC warning "..."
accepts a *single string literal*, and nothing else, on the line after
"warning". This is true in all versions of GCC and all versions of
clang that I have tested. Moreover, `_Pragma()` itself is specified
*not* to perform string literal concatenation on its argument (C99
§6.10.9: the syntax allows only a single string-literal token). The
current deprecation message is a full paragraph of text containing the
name of the deprecated macro in three places:
In the GNU C Library, `major' is defined by <sys/sysmacros.h>.
For historical compatibility, it is currently defined by
<sys/types.h> as well, but we plan to remove this soon.
To use `major', include <sys/sysmacros.h> directly.
If you did not intend to use a system-defined macro `major',
you should #undef it after including <sys/types.h>.
With `__attribute__ ((deprecated ("...")))` we can do this easily
using string-literal concatenation, but the only way to do it with
`_Pragma` is to define the message as *unquoted* text, something like
#define deprecation_msg(symbol) \
In the GNU C Library, #symbol is defined by\n\
<sys/sysmacros.h>. For historical compatibility, it is currently\n\
defined by <sys/types.h> as well, but we plan to remove this soon.\n\
To use #symbol, include <sys/sysmacros.h> directly. If you did not\n\
intend to use a system-defined macro #symbol, you should undefine it\n\
after including <sys/types.h>.
#define pragma_warning(...) pragma_warning_ (__VA_ARGS__)
#define pragma_warning_(...) pragma_warning__ (GCC warning #__VA_ARGS__)
#define pragma_warning__(...) _Pragma (#__VA_ARGS__)
#define major(m) \
pragma_warning (deprecation_msg (major)) \
gnu_dev_major (m)
And of course the problem with this is that all of the words in the
message are now exposed to macro expansion. (Also we can't say
`#undef` anymore, but that's minor.) For instance,
#include <sys/types.h>
#define it antidisestablishmentarianism
struct A {
unsigned major;
A (unsigned major) : major (major) {}
};
produces
test.cc:5:13: warning: In the GNU C Library, "major" is defined by
<sys/sysmacros.h>. For historical compatibility,
antidisestablishmentarianism is currently
defined by <sys/types.h> as well, but we plan to remove this soon.
To use "major", include <sys/sysmacros.h> directly. If you did not
intend to use a system-defined macro "major", you should undefine
antidisestablishmentarianism
after including <sys/types.h>.
which might leave people very confused indeed. (Even more hilarity
would ensue if the interfering macro name was "n".)
I have been unable to think of any way around this problem. The only
contexts in which preprocessor macro arguments are not macro-expanded
to exhaustion are when they are subject to either # (stringify) or ##
(token paste). Stringification would look ridiculous...
test.cc:26:13: warning: "In" "the" "GNU" "C" "Library," "major"
"is" "defined" "by" "<sys/sysmacros.h>." "For" "historical" ...
... and could not be used on the newlines. (clang word-wraps these
messages for us, but GCC doesn't, so we need the newlines.)
Token-pasting doesn't work here, because in order to get
`deprecation_msg` expanded, we use it in a *non*-noexpand argument,
which causes it to be "completely macro replaced." A construct like
#define is antidisestablishmentarianism
#define NX(a,b) a##b
#define deprecation_msg(symbol) #symbol NX(,is) NX(,deprecated)
would be expanded to `"symbol" is deprecated` and then expanded
*again* to `"symbol" antidisestablishmentarianism deprecated`.
One remaining option is to abandon all this mucking around with macros
and just write
#define major(m) \
_Pragma("GCC deprecated \"In the GNU C Library, \"major\" is defined by\n\
<sys/sysmacros.h>. For historical compatibility, it is currently\n\
defined by <sys/types.h> as well, but we plan to remove this soon.\n\
To use \"major\", include <sys/sysmacros.h> directly. If you did not\n\
intend to use a system-defined macro \"major\", you should #undef it\n\
after including <sys/types.h>.") \
gnu_dev_major (m)
but then the text has to be repeated not-quite-verbatim three times,
which I would prefer to avoid.
It's been a long time since I had to have the preprocessor
specification memorized, and it's quite possible that I have forgotten
a clever trick that would get around all these obstacles. Can anyone
else think of one? Failing that, I'd like to invite suggestions for a
*shorter* deprecation message, one that would not be such a problem to
repeat three times.
zw
More information about the Libc-alpha
mailing list