Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generalize C++11 support for C++14, C++17, etc. #127

Closed
bartlettroscoe opened this issue Jun 7, 2016 · 9 comments
Closed

Generalize C++11 support for C++14, C++17, etc. #127

bartlettroscoe opened this issue Jun 7, 2016 · 9 comments

Comments

@bartlettroscoe
Copy link
Member

Description:

Currently, TriBITS provides the global option ${PROJECT_NAME}_ENABLE_CXX11 and if TRUE, will look for the right compiler flag that gets a trial C++11 program to build successfully.

It is desired (mostly by Trilinos developers) to provide the same support for C++14, C++17, and other future C++ standards as they are being defined.

The goal of this Story is to refactor the current TriBITS support for C++11 and then make it easy to add support other later C++ standards. This should be pretty simple:

  1. Change TribitsCXXSupport.cmake to TribitsCxxStandardSupport.cmake and pass in the name of the standard CXXSTD, i.e. CXX11, CXX14, CXX17, etc.

  2. For each C++ standard, define a trial C++ program that checks for support for that standard. Define the variable ${PROJECT_NAME}_${CXXSTD}_TRIAL_PROGRAM to that program, and allow projects to override it (through ${PROJECT_NAME}_${CXXSTD}_TRIAL_PROGRAM_DEFAULT). By allowing a project to override the trial program, then they can customize the checks.

  3. For each C++ standard, define the ordered list of compiler flags ${PROJECT_NAME}_${CXXSTD}_COMPILER_FLAGS_TO_TRY to try with the trial program. Allow project to define the default ${PROJECT_NAME}_${CXXSTD}_COMPILER_FLAGS_TO_TRY_DEFAULT but have TriBITS give the default.

  4. For all variables with CXX11 explicitly in the name, replace with ${CXXSTD} input argument to all functions/macros.

One issue that we need to address is what do we do about the progression of C++ standards. For example, if C++17 is enabled, do we just check for the compiler option for C++17 by running the trial program for C++17 and then assume that automatically C++14 and C++11 are also enabled and will also work? Or, if C++17 is enabled, do we just find the compiler flags for C++17 and then go back and check the trial programs for C++14 and C++11 and make sure that they still work? It makes no sense to assume that C++17 is enabled and then not assume that C++14 and C++11 are not. These standards are cumulative and I don't think hardly every break backward compatibility. In cases where the standards are not backward compatible, TriBITS packages and code can see what standards are enabled and then make adjustments. For example, if some feature was introduced in C++11 but was changed in C++17, then logic in the C++ source files could look at HAVE_<UCPACKAGE>_CXX11 and HAVE_<UCPACKAGE>_CXX17 at pre-process time and then adjust accordingly.

If we adopt the behavior where we always run the trial programs for the earlier C++ standards, then that will make configure take longer (i.e. bad). But it has the advantage of not requiring the later standard trial programs to have to duplicate the checks for the earlier standards (i.e. good). My (Ross's) opinion is that the extra checks are likely worth it since it avoids duplication in the trial C++ programs.

Tasks:

  1. Get consensus on what the behavior should be w.r.t. the enables of later standards being enabled on earlier standards. Should the trial programs for earlier standards be run or should the project just assume that earlier standards are also satisfied?
  2. Refactor existing C++11 support code in TriBITS to parameterize on CXXSTD input.
  3. Get initial trial programs for C++14 and C++17 from Trilinos and Exnihilo developers. Also, get example of Trilinos code that would have a dependence on C++14 and C++17.
  4. Add support for C++14 and C++17 in TriBITS and test out under Trilinos, doing all of this on branches.
  5. Commit updated TriBITS code to GitHub 'master' and snapshot into Trilinos 'master'.
@carlos-stellar
Copy link

There appears to be a trend to move away from checking for conformance with specific versions of the C++ standard towards checking for availability of specific features. I've included some relevant links below. This idea is not new to client-side JavaScript development (link below at the bottom).

Before effort is put into detecting C++14, C++17, etc, I wonder if a different approach using feature detection might work better in the longer term.

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0096r1.html

http://en.cppreference.com/w/User:D41D8CD98F/feature_testing_macros

https://cmake.org/cmake/help/v3.6/manual/cmake-compile-features.7.html

http://diveinto.html5doctor.com/detect.html

@bartlettroscoe
Copy link
Member Author

@carlos-stellar, thanks for the perspective. This is a complex issue. In a perfect world, you should just be able to assume that a compiler is all C++11, C++14, ..., compliant and then use all of the features that that standard says it should support. But of course reality is messier.

The approach that we are taking in this ticket is to give a name "C++XY" to a set of desired C++ features for a given C++ project. That means fewer ifdefs in your C++ code but it also means that your C++ code needs to agree on what features of C++XY provides. That is why this system will allow a given C++ project to supply its own project-specific configure-time checks.

But one could make an argument, as you do above, that we should instead do this on a feature-by-feature basis with configure-time checks like documented here and then use those to define macros in configured header files. That is something worth considering for sure.

In reality, however, the little trial programs that you write for the configure-time feature tests don't really fully test support for a given C++ feature. Just because some configure-time test says that the given compiler supports variatic templates, for example, does not mean that that compiler will support how the real C++ code uses that feature. Compilers have bugs for features that they claim to support (and that work in simple test programs).

Therefore, in the end, you just have to do testing for many different compiler vendors and different compiler versions and iteratively tweak your configure-time checks for a set of given features and your source code's usage of those sets of features. The portability testing and maintenance process is never going to change.

@carlos-stellar
Copy link

Thank you for the explanation.

The TriBITS Reference Guide, 2016-06-24, section 4.5, contains this:

“By default, the system will try to automatically find compiler flags
that will enable C++11 features. If it finds flags that allow a test
C++11 program to compile, then it will an additional set of
configure-time tests to see if several C++11 features are actually
supported by the configured C++ compiler and support will be disabled if
all of these features are not supported.”

Searching through the TriBITS and Trilinos sources, I could not find
anything that looked like an implementation of the above paragraph.
Maybe the text describes an obsolete approach to the problem?

From a quick look at the Kokkos build files (see for example
Trilinos/packages/kokkos/core/src/CmakeLists.txt), they appear to
consider the availability of a C++11 command line switch as enough
evidence that the C++ features they need are present. I saw no mention
of specific compiler versions, in comments or otherwise, in this code.
So I assume it is depending on the published minimum compiler versions
for the Trilinos project as a whole, as you explain in your email. There
appears to be no attempt to document or require specific C++ features or
specific compiler versions on a package by package basis.

Regardless, your reasoning is sound. I am just trying to reconcile
everything that I'm seeing.

Carlos Reyes
1-505-206-1569 (mobile)

On 09/09/2016 05:11 PM, Roscoe A. Bartlett wrote:

@carlos-stellar https://github.com/carlos-stellar, thanks for the
perspective. This is a complex issue. In a perfect world, you should
just be able to assume that a compiler is all C++11, C++14, ...,
compliant and then use all of the features that that standard says it
should support. But of course reality is messier.

The approach that we are taking in this ticket is to give a name
"C++XY" to a set of desired C++ features for a given C++ project. That
means fewer ifdefs in your C++ code but it also means that your C++
code needs to agree on what features of C++XY provides. That is why
this system will allow a given C++ project to supply its own
project-specific configure-time checks.

But one could make an argument, as you do above, that we should
instead do this on a feature-by-feature basis with configure-time
checks like documented here
https://cmake.org/cmake/help/v3.6/manual/cmake-compile-features.7.html
and then use those to define macros in configured header files. That
is something worth considering for sure.

In reality, however, the little trial programs that you write for the
configure-time feature tests don't really fully test support for a
given C++ feature. Just because some configure-time test says that the
given compiler supports variatic templates, for example, does not mean
that that compiler will support how the real C++ code uses that
feature. Compilers have bugs for features that they claim to support
(and that work in simple test programs).

Therefore, in the end, you just have to do testing for many different
compiler vendors and different compiler versions and iteratively tweak
your configure-time checks for a set of given features and your source
code's usage of those sets of features. The portability testing and
maintenance process is never going to change.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#127 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/ASqzLJ6VgVBNYOQG_Yefq6azXxGFXtgXks5qoeelgaJpZM4IvgUJ.

@bartlettroscoe
Copy link
Member Author

@carlos-stellar, the documentation you reference at:

looks to be pretty close to what is implemented in:

The only difference is that if the trial C++11 programs don't compile, it errors out with a fatal error.

This particular Story is to generalize this code as described above in the Description field.

I have not looked at what Kokkos is doing with C++11 but it sounds like it is not keying off of this TriBITS-provided support.

Someone should likely call a meeting with the Kokkos and Tpetra developers and key stakeholders to discuss a rational strategy for managing C++11, C++14, C++17, etc. support going forward.

What is your driver for interest in this Story?

@carlos-stellar
Copy link

Thank you! This is all of a sudden making a lot more sense.

Right now I am just trying to understand TriBITS/Trilinos better. I do
not have any particular interest in this story, beyond making sense of
it in the bigger context of the software and documentation.

Carlos Reyes

1-505-206-1569 (mobile)

On 09/12/2016 08:33 AM, Roscoe A. Bartlett wrote:

@carlos-stellar https://github.com/carlos-stellar, the documentation
you reference at:

looks to be pretty close to what is implemented in:

The only difference is that if the trial C++11 programs don't compile,
it errors out with a fatal error.

This particular Story is to generalize this code as described above in
the Description field.

I have not looked at what Kokkos is doing with C++11 but it sounds
like it is not keying off of this TriBITS-provided support.

Someone should likely call a meeting with the Kokkos and Tpetra
developers and key stakeholders to discuss a rational strategy for
managing C++11, C++14, C++17, etc. support going forward.

What is your driver for interest in this Story?


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#127 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/ASqzLE6XKvLUMdRX18uus5IeKiWxRJL5ks5qpWLUgaJpZM4IvgUJ.

@bartlettroscoe
Copy link
Member Author

Given that newer CMake versions have built-in support for different C++ standards, I think we should just drop this feature from TriBITS and just use raw CMake support for this. Actually, for Trilinos, it uses Kokkos with KOKKOS_ARCH to determine this stuff.

TriBITS needs to get put of the business of setting compiler options.

@bartlettroscoe
Copy link
Member Author

Brad King and I had a long discussion on this topic in :

Basically, TriBITS needs to completely rip out any directly handling of C++ standards and just use set(CMAKE_CXX_STANDARD <stdyear>) and target_compile_features(<lib> PUBLIC cxx_std_<stdyear>)

So a project would set:

std(CMAKE_CXX_STANDARD <stdyear>)

where <stdyear> is 11, 14, 17, 20, etc. when one wants to say "use C++14", for example, but don't allow any code that requires newer standards.

And one would set:

target_compile_features(<lib> PUBLIC cxx_std_<stdyear>)

on any library that you want to tell CMake to make sure that the supports say C++14 but also allow the compiler to support newer standards as well.

@bartlettroscoe
Copy link
Member Author

This was completed with the merge commit 7579e18 and its preceding commits to use built-in CMake support for C++ standards with the cache var CMAKE_CXX_STANDARD and target_compile_features(<libname> PUBLIC <cxx-std>).

Closing this as complete.

@bartlettroscoe
Copy link
Member Author

Related to:

  • SEPW-213
  • SEPW-214

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants