[SG10] Comments from Alisdair

Stephen Kelly steveire at gmail.com
Sat May 31 11:47:18 CEST 2014


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


More information about the Features mailing list