This is the mail archive of the
libc-alpha@sourceware.org
mailing list for the glibc project.
Re: RFC: handling ISO C feature test macros
- From: Roland McGrath <roland at hack dot frob dot com>
- To: Joseph Myers <joseph at codesourcery dot com>
- Cc: <libc-alpha at sourceware dot org>
- Date: Fri, 27 May 2016 20:03:32 -0700 (PDT)
- Subject: Re: RFC: handling ISO C feature test macros
- Authentication-results: sourceware.org; auth=none
- References: <alpine dot DEB dot 2 dot 20 dot 1605202135440 dot 24104 at digraph dot polyomino dot org dot uk>
The original intent of features.h was that it and only it would ever
need to know the complex interdependencies between feature-test sets.
Each conditional around an actual declaration can have a very simple
#if test on exactly one fine-grained set. In the early days, this was
actually completely true (modulo some __FAVOR_BSD complexities). We
have drifted away from the ideal over the years and now have over 300
cases of "#if defined __USE_THIS || defined __USE_THAT" and the like.
But I advocate only schemes that take us back towards that ideal
rather than farther away from it.
So between your proposals I prefer (b) over (a).
But I don't think exactly either of them is the way to go today.
Nowadays I also advocate only schemes that are -Wundef-friendly and
typo-proof. That means avoiding anything based on #ifdef or defined in
the headers doing the actual declarations.
I'm also quite sympathetic to Paul's concern for comprehensibility to
the casual reader of installed headers. (I'm the one who wrote all the
semantics-documenting comments on every function declaration in the
first place, after all.) We should certainly endeavor to make it as
easy as possible to understand what the real condition is. But IMHO
that is always secondary to the maintainability for us, which requires
simplicity and typo-proofing at the site of declarations.
While I'm stating relevant principles, I also want to point out that
features.h and __USE_* macros were always intended as internal
implementation details. Unfortunately, they have leaked out to where
some application and library code does '#include <features.h>' itself
and even looks at __USE_* macros. In whatever we do for the future, we
should avoid providing more of such ammunition for users to point at
their feet (and for library maintainers to point at their users' feet).
Ideally, we should eventually redress the unintended fallout from the
past and eliminate <features.h> and its unfortunately well-known macros
from the de facto API. The upshot here is that new stuff should avoid
friendly-seeming header file names like <features-iso.h> and instead do
something that is very hard to misconstrue as anything other than
internal implementation details that nobody outside libc's own headers
should ever be using.
So (as usual) I want to (not) answer your question about a small
specific issue by widening it to say you should revamp a much larger
area of the code in a way that cleans up the world while also making
your small specific issue a straightforward instance of the bigger
general case. But, of course, I don't mean that you have to fix the big
problem to be able to fix the little problem. I really just mean that
we should brainstorm about where we want to end up for the larger issue
enough to be fairly confident that we're addressing the specific small
issue in a way that will fit well into the eventual cleaned-up future.
Fortunately for the "test exactly one set" principle, the new ISO C
feature-test sets seem to come in small, fine-grained, non-overlapping
sets to begin with. This should make it easy to use something like:
#if __GLIBC_USE (IEC_60559_BFP_EXT)
declare issignaling
#endif
A style that is as simple as this has pretty good casual readability IMHO.
I think it also helps that readability to use identifiers that are
exactly taken (modulo the nonvarying prefix and suffix) from the ISO C
macro names. (If the outermost layer of macro helper uses ## immediately
on the macro argument, name space safety doesn't even require using a
prefix like __ on these identifiers. I'm pretty sure of that, anyway.)
Fortunately for generally minimizing the complexity of it all, since the
demise of __FAVOR_BSD, I think _GNU_SOURCE really does now mean
absolutely everything, the superset of all feature-test sets.
Unfortunately for readability and simplicity, the interactions of the
-std= modes and standard-specified feature-test macros are still hairy.
If we were to tease these down to individual sets so every declaration
could test just one thing, we'd have a bunch of fiddly little sets that
could never have names that are intrinsically meaningful in any way
useful to casual readers.
Another thing I'll raise now is that some users actually want their code
to figure this stuff out too and the situation for trying to do it today
really sucks. We have long said that best practice is empirical tests
in the Autoconf style. But some people really don't want to do that,
have a hard time with Autoconf or have contrary opinions or principles
about the best ways to do this. People writing installed headers for
libraries sometimes need to have a single installed header that can cope
with multiple libc versions and/or multiple feature-test macro states
that the header might need to work with. Today people either just get
this wrong, so their headers require a particular feature-test macro
state (of which they might not even be aware); or they try to test all
the API feature-test macros and reimplement all the complex combined
tests correctly to match what libc's headers are doing (and getting that
100% right is probably as rare as some animals that haven't been
observed in the wild for decades); or they do unspeakable things with
testing our internal macros. So we'd do our users a great service by
providing a first-class, supported API to solve these problems. Perhaps
we can design something for ourselves that can be easily extended later
to provide a good user API with marginal additional work.
For a user API it seems pretty clear that the only thing simple enough
to use is a predicate for exactly what the user wants to know:
#if LIBC_HAS (foobar)
... we can use foobar! ...
#endif
That is, LIBC_HAS (foobar) answers the question, "Is foobar available to
me in the current feature-test macro state?" (It is probably sufficient
to have a single interface for all entities that one might test for, be
they functions, global variables, enum constants, types, or whatever.
But perhaps there is some reason to have separate interfaces for
LIBC_HAS_FUNCTION (foo) and LIBC_HAS_TYPE (bar_t) and so on.)
Straw man proposal: use this same style to control what we declare in
our headers too.
The internal version probably has to be slightly more fiddly than this,
for at least one issue I've thought of so far. For some historical API
case we support, we have to answer the question, "Should foo.h declare
bar?" where the answer is sometimes different for the same value of bar
and different values of foo. (There is at least one such case, though
I've forgotten exactly what it is. I'm not sure there is actually more
than one, but let's call it a general requirement for this straw man.)
So perhaps it's:
/* in foo.h */
#if __glibc_has (foo, bar)
declare bar
#endif
That is very easy to maintain in foo.h itself, and very easy to see
looking at it what it means. It certainly does not meet Paul's
criterion that the casual reader can readily know what the actual
feature-test API conditions for bar in foo.h are just by reading around
the declaration of bar in the installed foo.h file. But perhaps we can
provide sufficiently good means to know that stuff that all the other
benefits of this approach trump that particular criterion.
Implementing this purely in hand-written macros in giant headers akin to
features.h is even less maintainable than the status quo. What I
imagine instead is that we'd maintain a single "source of truth" for the
name space controls in something along the lines of the conform/data/
files. From that one place to maintain the data, we can get many things
automagically:
* generate header files (be it one or many) used in the implementation
details of __glibc_has
* generate or cross-check that information in the manual (and make it
easy to do the same for linux man pages, though that is not directly
our problem)
* drive our conform and link name space tests
* generate additional quick-reference documentation, e.g. a flat simple
text file that could be installed alongside headers in /usr/include
that shows the feature-test macro states required for each symbol,
which might be close to as easy to consult without real proper
documentation on hand as just reading the headers themselves (this
being something that you always have installed if you have the headers
themselves, unlike the manual)
* other handy things I haven't thought of just now
And, of course, fixing one of these header name space bugs becomes a
single change in a single file that rules them all. (The only other
change needed would be for the case where a symbol was declared
unconditionally and it needs the #if __glibc_has (...) added around its
declaration site.)
Thanks,
Roland