This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

following up on bug 19239 - verbose deprecation warnings .vs. the C preprocessor


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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]