C++ Logo

SG10

Advanced search

Subject: Re: [SG10] Comments from Alisdair
From: Stephen Kelly (steveire_at_[hidden])
Date: 2014-05-31 04:47:18


Nelson, Clark wrote:

> Unquestionably, __has_feature had a significant influence on SG10 and our
> recommendations. The main influences had to do with the discipline of
> thinking about the impact of adding features, and about granularity (what
> "a feature" really is).
>
> The fact that we went with a different syntax has to do with low-level,
> nitty-gritty details. It was absolutely *not* due to a lack of
> appreciation or gratitude, nor even from a desire to put our own stamp on
> things.

I just want to first respond to this part. I don't have an agenda of trying
to convince you to recommend __has_feature, for gratitude, validation or any
other reason. I don't have any strong connection to Clang development and my
satisfaction isn't going to depend in any way on whether you recommend
__has_feature :).

I'm only trying to understand why it is not recommended. Many mortals like
myself read the Nxxxx standard proposal documents and documents like SD6
while fewer try to read the whole standard. Many of those such documents
include lots of rationale (as does SD6 commendably), not only changes to
standardese.

My understanding falls short in trying to understand the difference in
(possible) recommendation of __has_cpp_attribute and the non-recommendation
of __has_feature, where recommending it would seem to be consistent. If it's
too much, just let me know and I'll stop trying to understand. Thanks for
your response so far!

> Once it's possible to recognize a
> __has_include construct, its implementation is trivial.

> But __has_feature would require a brand-new kind of integration between
> the preprocessor and the compiler proper. Remember that the original
> preprocessor finished running before the compiler proper was even invoked.

> In a design like that, a compiler would have to make available to the
> preprocessor, presumably before invoking it, a table of all the features
> it supports. That was just too much novelty for SG10, which is by its
> nature a group that cares very much about backward compatibility.

Aha! I'm starting to understand. A preprocessor can be taught to recognize
__has_feature, but it would either

1) Need a new interface to allow the compiler driver to specify which
features are present. Eg, something equivalent to

 $PP -features "cpp_constexpr,cpp_variadic_templates" main.cpp -o main.i

and you don't want to require such a new interface

or

2) Would require hardcoding in the preprocessor and tight coupling and
bundled releases of the preprocessor, the compiler( driver) and you don't
want to require that.

Is my re-statement correct?

Instead, a compiler driver can invoke something equivalent to

 $PP -D__cpp_constexpr=201304 main.cpp -o main.i

which is something preprocessors are already designed to do and have an
interface for.

So far, I understand all this. Thanks for explaining.

> Of course the same is true about __has_cpp_attribute.

Exactly, so I'm back to not understanding :). This would seem to require a
new interface in the preprocessor, an idea already rejected above.

> But there are two
> practical differences between __has_[cpp_]attribute and __has_feature. One
> is that, for an attribute, the "key" to refer to a specific item is
> basically given; for a general feature, the key needs to be invented
> separately.

You mean the attribute would be

 [[deprecated]]

or

 [[noreturn]]

and the preprocessor would only have to recognize the same text inside
__has_cpp_attribute() ?

I don't see how that makes any difference. The preprocessor does not have to
know the "meaning" of 'cpp_constexpr' in order to recognize
__has_feature(cpp_constexpr).

Presumably if a new interface in the preprocessor can be added easily for
something equivalent to

 $PP -attributes "deprecated,noreturn" main.cpp -o main.i

then something equivalent to

 $PP -features "cpp_constexpr,cpp_variadic_templates" main.cpp -o main.i

is just as easy?

Am I missing something in your point here?

Also, regarding the key being basically given, one standard might
standardize [[foo]] and __has_cpp_attribute(foo) would work.

A future standard might standardize [[foo(msg)]] and
__has_cpp_attribute(foo_with_message) would work. So, what is true about
attributes now might not always be true, unless it is intended and designed
to maintain the truth of it.

> The other is the common expectation that an unrecognized
> attribute will be ignored, and not an error.

Are you making the point that someone can write

 #if __has_cpp_attribute(deprecated)
 [[deprecated]]
 #endif
 int foo() { /* ... */ }

and everything is fine, if the compiler supports attributes. Further,
someone can write:

 [[deprecated]] int foo() { /* ... */ }

and everything might still be fine, even if the compiler does not support
the particular attribute, because the compiler might by default not report
an error even though the particular attribute is not known.

However, there is an expectation that a compiler will report a hard-error if
a user writes

 constexpr int foo() { /* ... */ }

without guarding the 'constexpr'.

So, __has_cpp_attribute is 'safer' in the face of omission. Is that the
point you are making?

As true as that might be, how does it affect the requirement on the
interface of the preprocessor?

>> > To put it more bluntly, SG10's purpose is to make it possible
>> for a
>> > program to detect what new features are present in an
>> implementation. If
>> > we had made a recommendation that depended entirely on the
>> existence of
>> > yet another new feature, we would have built a castle in the
>> air.
>>
>> I don't understand this either I'm afraid :(.
>>
>> Are you suggesting that something similar to the __has_include
>> example in
>> the document can not work or is fundamentally undesirable?
>>
>> #ifdef __has_feature
>> # if __has_feature(cpp_variadic_templates)
>> # define BOOST_HAVE_VARIADIC_TEMPLATES
>> # endif
>> #endif
>
> Not at all. But consider the equivalent code using SG10's recommendations:
>
> #if __cpp_variadic_templates
> #define BOOST_HAVE_VARIADIC_TEMPLATES
> #endif
>
> Obviously, it's simpler to write, since you don't need to check to see if
> __has_feature is available before using it.

That's true. However, here's a thought experiment:

Let's assume that the day has arrived when I can assume that I no longer
have to do something complex like

 #if __clang__
 // ...
 #elif __GNU__
 // ...
 #elif _MSC_VER
 // ...
 #endif

and I can rely on all implementations used with my code having the 'correct'
answer for the presence of the __cpp_variadic_templates macro and use your
code above.

If there is a future when such a day will arrive, is there not also a future
when I can use __has_feature without first testing if it is defined?

On that day, the __has_feature() solution and the __lib_cpp macro solution
are equally complex code-wise.

>From today until that day, they are also both almost equally complex:

 #if defined(__cpp_constexpr) && __cpp_constexpr
 // ...
 #elif __clang__
 // SD6 recommends __cpp_constexpr for what Clang calls
 // cxx_relaxed_constexpr
 #if __has_feature(cxx_relaxed_constexpr)
 #endif
 #elif __GNU__
 // ...
 #elif _MSC_VER
 // ...
 #endif

versus

 #if defined(__has_feature)
 #if __has_feature(cpp_constexpr) || __has_feature(cxx_relaxed_constexpr)
 // ...
 #endif
 #elif __GNU__
 // ...
 #elif _MSC_VER
 // ...
 #endif

(which looks slightly less complex to me, but not enough to argue about).

> And it doesn't require a
> specially modified preprocessor; it uses only standard preprocessor
> features.

Right, as discussed above.

Thanks,

Steve


SG10 list run by sg10-owner@lists.isocpp.org