[libcxx] RFC: C++14 and beyond

libcxx was begun as a C++11 (then called C++0x) library. It never even tried to adhere to the C++03 specification, though it does endeavor to work in C++03 language mode.

Now we have a working draft for C++14 and certainly libc++ needs to support C++14. However not all C++14 changes are 100% API compatible with C++11 (just like there are a few API breakages between C++03 and C++11). For example John Spicer demonstrated the following example which shows that you can silently impact run time behavior by adding constexpr to a function.

#if 0
constexpr
#endif
int g(int) { return 1; }

template <int I> struct A {};

template <class T> auto f(T*) -> decltype(A<g(T())>())*; // called if g is constexpr
template <class T> int f(T); // called if g is not constexpr

int
main()
{
    int *p = 0;
    f(p);
}

This indicates that even the post-C++11 additions of constexpr to chrono::time_point and tuple have the potential to break code.

This isn't an argument for not adding post-C++11 items to libc++. It is simply an argument that we should provide a way for platforms and clients to choose:

1. What is the default C++ standard libc++ is going to adhere to for a given platform?

2. How do clients override the default?

And I believe we should create a scalable solution beyond just the choice between C++11 and C++14. C++17 is just around the corner. And we should, at least for now, avoid macro names which allude to the numbers 14 and 17 as there is no telling whether the committee will keep its schedule or not (it doesn't have a very good track record).

I would like to open this topic up for discussion as I'm already aware of two post C++11 contributions that are blocked on this issue.

Thanks,
Howard

– Matthieu

Hello,

This isn't an argument for not adding post-C++11 items to libc++. It is simply an argument that we should provide a way for platforms and clients to choose:

1. What is the default C++ standard libc++ is going to adhere to for a given platform?

The same standard that clang uses for compilation (-std=c++xx). Not sure if that would work with
gcc, though.

This might also be a problem when libc++ is shipped with the OS (as is the case on MacOS X).

2. How do clients override the default?

By appropriately defining a configuration macro (like the already existing _LIBCPP_DEBUG2).

How do the inline namespaces that libc++ already uses play into this?

My 2 cents,
Jonathan

Just MHO:

You should clearly use some sort of macro to control the behavior of the headers, and (to support future standards) it should be something like __cplusplus that is defined to a number.

A harder question is: how should this be surfaced by the clang driver? I don't think it makes sense to force people to pass a -D flag to control this. This is the sort of thing that should have driver support. It seems that we could:

1) implicitly tie it to the language setting (i.e., use __cplusplus directly) which is simple, but limiting, and goes against how libc++ works with '98 and '11.
2) add a new option, like "-stdlib-version=14" (or something hopefully better)
3) tie it into the -stdlib option, so you could use -stdlib=libc++14 or something.

Given that most people will generally want to use libc++'14 support with C++'14 language support, perhaps the right option is a hybrid of #1 and #2. We could have __cplusplus set the default mode for the library, but then add the new option which would let people override it in the uncommon case where they want C++'14 language but C++'11 library support.

What do you think?

-Chris

Hello,

> This isn't an argument for not adding post-C++11 items to libc++. It is
simply an argument that we should provide a way for platforms and clients
to choose:
>
> 1. What is the default C++ standard libc++ is going to adhere to for a
given platform?

The same standard that clang uses for compilation (-std=c++xx). Not sure
if that would work with
gcc, though.

This might also be a problem when libc++ is shipped with the OS (as is the
case on MacOS X).

> 2. How do clients override the default?

By appropriately defining a configuration macro (like the already existing
_LIBCPP_DEBUG2).

How do the inline namespaces that libc++ already uses play into this?

That's an interesting question, I think.

At what level of granularity should a client be able to "override" the
default ? Personally, given the absence of guarantees in the "which header
includes which other header" I believe that it should be set at library
level.

And I agree with Jonathan that probably this should be reflected in an
inline namespace "bump" to avoid accidental inconsistencies.

-- Matthieu

How do you detect that a compilation was invoked with the “std=c++1y” flag?
The traditional way of doing that is by inspecting the preprocessor define __cplusplus, but the value for that has not been determined for C++14.

tot clang and gcc 4.8 both use “201103” for -std=c++11 and -std=c++1y
[ I expect this will change by the time C++14 is adopted, but that’s what it is today. ]

– Marshall

Marshall Clow Idio Software <mailto:mclow.lists@gmail.com>

A.D. 1517: Martin Luther nails his 95 Theses to the church door and is promptly moderated down to (-1, Flamebait).
– Yu Suzuki

Hello,

Regarding the numbering... at the moment "C++14" are enabled in gcc by using -std=c++1y (and I believe in Clang too), maybe using 1Y for now and changing it to 14 (or 15) once the Standard is finalized would work ?

How do you detect that a compilation was invoked with the "std=c++1y" flag?
The traditional way of doing that is by inspecting the preprocessor define __cplusplus, but the value for that has not been determined for C++14.

As far as clang is concerned this could be handled by __has_feature, e.g. __has_feature(cpp1y).
(or some other identifier that doesn't have the "ell" vs "one" disambiguation problem.)

Jonathan

libcxx was begun as a C++11 (then called C++0x) library. It never even tried to adhere to the C++03 specification, though it does endeavor to work in C++03 language mode.

Now we have a working draft for C++14 and certainly libc++ needs to support C++14. However not all C++14 changes are 100% API compatible with C++11 (just like there are a few API breakages between C++03 and C++11). For example John Spicer demonstrated the following example which shows that you can silently impact run time behavior by adding constexpr to a function.

#if 0
constexpr
#endif
int g(int) { return 1; }

template struct A {};

template auto f(T*) → decltype(A<g(T())>())*; // called if g is constexpr
template int f(T); // called if g is not constexpr

int
main()
{
int *p = 0;
f(p);
}

This indicates that even the post-C++11 additions of constexpr to chrono::time_point and tuple have the potential to break code.

My understanding based on casual conversation (not committee attendance/debate) was that user code was essentially told “don’t do this” and implementations were free to make more functions constexpr than the standard requires at any time. Is that the case?

Are there other types of incompatibilities in C++14 so far? I assume so, but just curious/checking.

This isn’t an argument for not adding post-C++11 items to libc++. It is simply an argument that we should provide a way for platforms and clients to choose:

  1. What is the default C++ standard libc++ is going to adhere to for a given platform?

Presumably it should be keyed off the language spec preprocessor defines provided by the compiler?

  1. How do clients override the default?

How much mismatch of library and language versions do you want to support. Seems to me that could get out of hand quickly. I realize you’re already doing this for usages of libc++ with a compiler in c++98 mode, but the multiplicative complexity might get out of hand quickly, no?

How does the implementation currently work to enable (or emulate) library features when compiling in C++98 mode? I assume that solution won’t scale well to multiple versions? (And, as you mentioned, isn’t built to actually just do 98 style, unlike the future where you want to do pure 11, rather than anything from 14 that works with 11 language features)

Is there much value in allowing users to enable C++14 libraries when compiling as C++11? Enough to justify the added complexity in the implementation of having to consider which features are backwards compatible and test them, etc?

I’m pretty sure that’s not the case.

In Portland, there was a session where Core and LWG got together and decided to hammer out this issue “once and for all”, since it kept coming up.

Three hours later, there was no consensus on the “correct” answer was, and the issue was tabled.

– Marshall

Marshall Clow Idio Software <mailto:mclow.lists@gmail.com>

A.D. 1517: Martin Luther nails his 95 Theses to the church door and is promptly moderated down to (-1, Flamebait).
– Yu Suzuki

How do you detect that a compilation was invoked with the "std=c++1y" flag?
The traditional way of doing that is by inspecting the preprocessor define __cplusplus, but the value for that has not been determined for C++14.

tot clang and gcc 4.8 both use "201103" for -std=c++11 and -std=c++1y
[ I expect this will change by the time C++14 is adopted, but that's what it is today. ]

***** clang Feature Request ******

This is a problem. At the moment, there is *no* difference in *any* compiler-supplied macro between -std=c++11 and -std=c++1y. We don't have to use __cplusplus to key on, but it would be nice to somehow detect the difference between -std=c++11 and -std=c++1y.

Hopefully, not at all. If at all possible, I would like to bump *only* the API in moving from C++11 to C++14, and not bump the ABI. Only if the committee forces us to bump the ABI, should we do so. An example of such forcing would be outlawing short-string-optimization, or something of that nature.

Howard

The prospect of clang driver support sounds good to me. And I like the options you lay out above. However I don't think we're at the point yet where we can ask the clang fe team to step in. First we need to prepare libc++ with the macro(s) that can be overridden, and then we can tell the driver how/when to override them.

Howard

So I'm thinking along these lines in <__config>, below where platforms customize things:

#ifndef _LIBCPP_STD_VER
# if 1 // -std=c++98/03/11
# define _LIBCPP_STD_VER 11
# elif 0 // -std=c++1y/14
# define _LIBCPP_STD_VER 13 // current year, or date of std ratification
# elif 0 // -std=c++17
   // ...
# endif
#endif // _LIBCPP_STD_VER

I can't make this work today because I can't tell the difference between -std=c++98/03/11 and -std=c++1y/14.

If the client (or platform) desires, they can #define _LIBCPP_STD_VER and to whatever they want and <__config> will respect their definition.

libcxx implementors can:

#if _LIBCPP_STD_VER > 11

// implement a post C++11 feature

#endif

The clang driver could be taught how to set _LIBCPP_STD_VER.

Thoughts? Help on telling the difference between -std=c++98/03/11 and -std=c++1y/14?

Thanks,
Howard

There’s been some discussion on the -admin reflector about what value we should use for __cplusplus, but it didn’t reach anything that looked like consensus. How about we use 201305L (ie, now) as the value for __cplusplus for our c++1y mode for the 3.3 release, and you key off __cplusplus > 201103L ?

That sounds fine with me. If there's any political problems with that, using a "throw away" macro (e.g. __experimental_1y, _MAKE_HOWARD_HAPPY) or whatever would be fine too. :slight_smile: libcxx can adapt to changing clangs.

Thanks,
Howard

Done.

Awesome, thanks!

Howard