__has_include rendered useless by compilers - clarification in the standard needed

1,093 views
Skip to first unread message

Marc Mutz

unread,
May 11, 2017, 5:25:08 AM5/11/17
to std-dis...@isocpp.org
Hi,

I've recently attempted to conditionally-include <string_view>, using the
<optional> example from P0061R1:

#if __has_include(<optional>)
# include <optional>
# define have_optional 1
#elif __has_include(<experimental/optional>)
# include <experimental/optional>
# define have_optional 1
# define experimental_optional 1
#else
# define have_optional 0
#endif

Unfortunately, GCC reports that the header is available even in C++14-mode,
but then #errors out when actually #including it, and there does not appear to
be any incentive to fix the situation:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79433

I'd like to suggest that, for all standard headers, C++17 mandates that
__has_include must not evaluate to 1 if the header is not availabe in that
version of the standard.

I know this is hairy, since the standard always assumes that all standard
features are available (that includes headers), and so wording would have to
reference future standards, but if you do nothing, then you might as well
remove the __has_include feature, because it becomes nearly impossible to use
it portably.

Suggested wording, to fix the situation at least for C++17+:

[....] has-include-expression evaluates to 1 if the search for the source
file succeeds, and to 0 if the search fails.

+ The search shall fail for all headers defined in future revisions of this
+ standard, and not already present in this version of the standard.

Or:

+ Implementations shall ensure that this search does not find headers added
+ only by future revisions of this standard.

This would not be an issue if all implementations supported SD-6, _and_ no
SD-6 macro, ever, was not either pre-defined or defined in a C++98 header,
such as cstddef. But right now, where the SD-6 macro for
experimental::string_view is defined in <experimental/string_view>,
__has_include returns true but #include fails, the whole SD-6 architecture,
even ignoring MSVC, is falling apart.

Thanks,
Marc

--
Marc Mutz <marc...@kdab.com> | Senior Software Engineer
KDAB (Deutschland) GmbH & Co.KG, a KDAB Group Company
Tel: +49-30-521325470
KDAB - The Qt, C++ and OpenGL Experts

Ville Voutilainen

unread,
May 11, 2017, 6:35:18 AM5/11/17
to std-dis...@isocpp.org
On 11 May 2017 at 12:23, Marc Mutz <marc...@kdab.com> wrote:
> Hi,
>
> I've recently attempted to conditionally-include <string_view>, using the
> <optional> example from P0061R1:
>
> #if __has_include(<optional>)
> # include <optional>
> # define have_optional 1
> #elif __has_include(<experimental/optional>)
> # include <experimental/optional>
> # define have_optional 1
> # define experimental_optional 1
> #else
> # define have_optional 0
> #endif
>
> Unfortunately, GCC reports that the header is available even in C++14-mode,
> but then #errors out when actually #including it, and there does not appear to
> be any incentive to fix the situation:
> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79433

There seems to be incentive to fix the SD-6 recommendation so that you
don't need
to rely on __has_include to try and fail to check whether a particular
library facility
is available, so that you can rather use a separate macro to check for
that library
facility, all based on that bug report and the related discussions.

Jonathan Wakely

unread,
May 11, 2017, 6:45:28 AM5/11/17
to ISO C++ Standard - Discussion


On Thursday, 11 May 2017 10:25:08 UTC+1, Marc Mutz wrote:
Hi,

I've recently attempted to conditionally-include <string_view>, using the
<optional> example from P0061R1:

 #if __has_include(<optional>)
 #  include <optional>
 #  define have_optional 1
 #elif __has_include(<experimental/optional>)
 #  include <experimental/optional>
 #  define have_optional 1
 #  define experimental_optional 1
 #else
 #  define have_optional 0
 #endif

Unfortunately, GCC reports that the header is available even in C++14-mode,
but then #errors out when actually #including it, and there does not appear to
be any incentive to fix the situation:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79433


There's no incentive to fix it the way you suggested, because I think we can do something better. And I've started discussions about doing so.
 
I'd like to suggest that, for all standard headers, C++17 mandates that
__has_include must not evaluate to 1 if the header is not availabe in that
version of the standard.

I know this is hairy, since the standard always assumes that all standard
features are available (that includes headers), and so wording would have to
reference future standards, but if you do nothing, then you might as well
remove the __has_include feature, because it becomes nearly impossible to use
it portably.

Suggested wording, to fix the situation at least for C++17+:

  [....] has-include-expression evaluates to 1 if the search for the source
  file succeeds, and to 0 if the search fails.

 + The search shall fail for all headers defined in future revisions of this
 + standard, and not already present in this version of the standard.

Or:

 + Implementations shall ensure that this search does not find headers added
 + only by future revisions of this standard.


Which would make it non-conforming for libc++ to provide <string_view> in C++11 mode. Why would you want to do that?

That doesn't seem like a good solution.


 
This would not be an issue if all implementations supported SD-6, _and_ no
SD-6 macro, ever, was not either pre-defined or defined in a C++98 header,
such as cstddef. But right now, where the SD-6 macro for
experimental::string_view is defined in <experimental/string_view>,
__has_include returns true but #include fails, the whole SD-6 architecture,
even ignoring MSVC, is falling apart.


Hyperbole much?

Thiago Macieira

unread,
May 11, 2017, 2:09:54 PM5/11/17
to std-dis...@isocpp.org
On Thursday, 11 May 2017 02:23:52 PDT Marc Mutz wrote:
> Unfortunately, GCC reports that the header is available even in C++14-mode,
> but then #errors out when actually #including it, and there does not appear
> to be any incentive to fix the situation:
> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79433

Alternatively, GCC could just allow that header to be used in pre-C++17 mode.
However, the header could legitimately assume that you are in C++17 mode when
you use it, and thus use features added in that edition of the standard...

--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center

Richard Smith

unread,
May 11, 2017, 3:49:35 PM5/11/17
to std-dis...@isocpp.org
I would argue that if, say, __has_include(<shared_mutex>) returns 1 when including that header does not make the std::shared_mutex functionality available, your implementation does not conform to SD-6: SD-6 says you get to use __has_include(<shared_mutex>) to detect if the implementation implements N3659, so you can't claim conformance if your implementation does not satisfy that condition.

So the question is, is SD-6 being unreasonable, or are implementations getting it wrong? The current consensus of the features study group appears to be the former.
 
Suggested wording, to fix the situation at least for C++17+:

  [....] has-include-expression evaluates to 1 if the search for the source
  file succeeds, and to 0 if the search fails.

 + The search shall fail for all headers defined in future revisions of this
 + standard, and not already present in this version of the standard.

Or:

 + Implementations shall ensure that this search does not find headers added
 + only by future revisions of this standard.

This would not be an issue if all implementations supported SD-6, _and_ no
SD-6 macro, ever, was not either pre-defined or defined in a C++98 header,
such as cstddef. But right now, where the SD-6 macro for
experimental::string_view is defined in <experimental/string_view>,
__has_include returns true but #include fails, the whole SD-6 architecture,
even ignoring MSVC, is falling apart.

Thanks,
Marc

--
Marc Mutz <marc...@kdab.com> | Senior Software Engineer
KDAB (Deutschland) GmbH & Co.KG, a KDAB Group Company
Tel: +49-30-521325470
KDAB - The Qt, C++ and OpenGL Experts

--

---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+unsubscribe@isocpp.org.
To post to this group, send email to std-dis...@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.

Nicol Bolas

unread,
May 11, 2017, 4:32:22 PM5/11/17
to ISO C++ Standard - Discussion

But that's basically saying that you cannot use `__has_include` to know if an implementation supports a particular standard library header. Which means that you have to rely on an optional feature (feature test macros), rather than a required language feature to do so.

Richard Smith

unread,
May 11, 2017, 5:16:51 PM5/11/17
to std-dis...@isocpp.org
You have to rely on an optional feature either way. The standard does not make any guarantees about whether a header is useful or meaningful in the case where __has_include(x) evaluates to 1, only that the header exists. So either you're relying on the vendor implementing the part of SD-6 that says "__has_include(<x>) can be used to detect whether your implementer implements <some feature>" or you're relying on the (future) part of SD-6 that says "__cpp_lib_x can be used to detect whether your implementer implements <some feature>".

Bo Persson

unread,
May 12, 2017, 2:08:14 AM5/12/17
to std-dis...@isocpp.org
On 2017-05-11 22:32, Nicol Bolas wrote:
> On Thursday, May 11, 2017 at 3:49:35 PM UTC-4, Richard Smith wrote:
>
> On 11 May 2017 at 02:23, Marc Mutz <marc...@kdab.com <javascript:>>
The implementation DOES support the feature, and the header IS present.

It's just that the user has disabled the feature by using -std=c++14 and
then somehow expect the header to go away. If you don't want to use new
headers, why go look for them?

In just a few years I expect to use __has_include to detect new C++20
headers, and don't want the compiler to lie and say No, just because it
doesn't yet implement all of the C++20 core language (so no -std=c++20
available).



Bo Persson

Thiago Macieira

unread,
May 12, 2017, 6:05:00 AM5/12/17
to std-dis...@isocpp.org
On Thursday, 11 May 2017 23:08:03 PDT Bo Persson wrote:
> The implementation DOES support the feature, and the header IS present.
>
> It's just that the user has disabled the feature by using -std=c++14 and
> then somehow expect the header to go away. If you don't want to use new
> headers, why go look for them?

The compiler could search different directories depending on which -std=
option is passed. That way, __has_include will work and no #error in the
header is necessary.

> In just a few years I expect to use __has_include to detect new C++20
> headers, and don't want the compiler to lie and say No, just because it
> doesn't yet implement all of the C++20 core language (so no -std=c++20
> available).

Sorry, I could not understand what you're saying.

As it stands today, if you don't pass -std=c++2x, #include'ing that header you
found with __has_include will cause an #error. That's why we do want the
compiler to say "no".

Matthew Woehlke

unread,
May 12, 2017, 9:05:22 AM5/12/17
to std-dis...@isocpp.org
On 2017-05-12 06:04, Thiago Macieira wrote:
> The compiler could search different directories depending on which -std=
> option is passed. That way, __has_include will work and no #error in the
> header is necessary.

...and on platforms with ~~decent file systems~~ symlinks, the overhead
of this is small.

> As it stands today, if you don't pass -std=c++2x, #include'ing that header you
> found with __has_include will cause an #error. That's why we do want the
> compiler to say "no".

Just to illustrate this point:

#include <string_view> // error

Why does it matter if the reason that line errors is because the header
doesn't exist vs. because the compiler won't let you use it? Either way,
the result is that I'd better not try to include that header.

--
Matthew

Andrey Semashev

unread,
May 12, 2017, 9:12:04 AM5/12/17
to std-dis...@isocpp.org
On 05/12/17 16:05, Matthew Woehlke wrote:
>
> Just to illustrate this point:
>
> #include <string_view> // error
>
> Why does it matter if the reason that line errors is because the header
> doesn't exist vs. because the compiler won't let you use it? Either way,
> the result is that I'd better not try to include that header.

I think the point is so that the user can do this:

#if __has_include(<string_view>)
#include <string_view>
using std::string_view;
#else
#include <boost/utility/string_view.hpp>
using boost::string_view;
#endif

Or replace Boost.StringView with any other home-grown implementation.

Nicol Bolas

unread,
May 12, 2017, 11:10:40 AM5/12/17
to ISO C++ Standard - Discussion, b...@gmb.dk

"The implementation" is not merely the executable; it is also the environment in which that executable executes. Or more specifically, the implementation includes the compiler options. If you say `-std=c++14`, then it should behave in accord with the C++ 14 standard.

So no, the implementation of GCC with the particular compiler option does not support the feature.

In just a few years I expect to use __has_include to detect new C++20
headers, and don't want the compiler to lie and say No, just because it
doesn't yet implement all of the C++20 core language (so no -std=c++20
available).

But why would you want it to? That's my question: what is the usefulness of `__has_include` returning true for headers that technically are on disk but cannot be used with that implementation? What good is it to have `__has_include(<string_view>)` return true if `#include <string_view>` will fail?

Thiago Macieira

unread,
May 12, 2017, 2:20:06 PM5/12/17
to std-dis...@isocpp.org
On Friday, 12 May 2017 06:05:17 PDT Matthew Woehlke wrote:
> On 2017-05-12 06:04, Thiago Macieira wrote:
> > The compiler could search different directories depending on which -std=
> > option is passed. That way, __has_include will work and no #error in the
> > header is necessary.
>
> ...and on platforms with ~~decent file systems~~ symlinks, the overhead
> of this is small.

That's not needed. I'm talking about additional search paths, something that
GCC already does:

$ gcc -v -E -xc++ /dev/null |& sed -n '/search starts here/,/End of search/p'
#include "..." search starts here:
#include <...> search starts here:
/usr/include/c++/7
/usr/include/c++/7/x86_64-suse-linux
/usr/include/c++/7/backward
/usr/lib64/gcc/x86_64-suse-linux/7/include
/usr/local/include
/usr/lib64/gcc/x86_64-suse-linux/7/include-fixed
/usr/lib64/gcc/x86_64-suse-linux/7/../../../../x86_64-suse-linux/include
/usr/include
End of search list.

It's adding the platform-specific dir and that "backward" that contains
hash_map, hash_set and strstream, so why not a c++14 and a c++17 one?

> > As it stands today, if you don't pass -std=c++2x, #include'ing that header
> > you found with __has_include will cause an #error. That's why we do want
> > the compiler to say "no".
>
> Just to illustrate this point:
>
> #include <string_view> // error
>
> Why does it matter if the reason that line errors is because the header
> doesn't exist vs. because the compiler won't let you use it? Either way,
> the result is that I'd better not try to include that header.

Because the string_view paper said that if __has_include(<string_view>) is 1,
then I should be able to #include and get std::string_view. As Ville or Nicol
put it, either the paper (and the whole SD-6) is unreasonable or the compiler
needs to be fixed. Looks like the former.
Reply all
Reply to author
Forward
0 new messages