How to get CMake to pass either std=c++14/c++1y or c++17/c++1z based on GCC version?
Asked Answered
T

3

70

GCC 4.x doesn't accept the --std=c++14 switch for C++14 code - it takes --std=c++1y instead. Later versions take --std=c++1z but (probably) not --std=c++17 which has not been set yet (writing this in 2016). Perhaps there are similar issues with C++11.

Does CMake have some facility (perhaps as a module) to pass the correct switch according to the GCC version?

Tore answered 16/3, 2017 at 13:0 Comment(3)
"Perhaps there are similar issues with C++11." That would be c++11 vs c++0x, the former being introduced in gcc4.7/8 or so.Layette
Btw, the old switches (0x, 1y, 1z) work just fine even on compilers that support the modern ones, so there is no real need to get this "right".Layette
There is a need to get this "right" since passing the raw compiler flags is a CMake anti-pattern and against the whole concept of a meta-build system/build system generator.Thadthaddaus
C
120

When wanting to specify a particular C++ version, the recommended way to do this with CMake 3.1 and later is to use the CXX_STANDARD, CXX_STANDARD_REQUIRED and CXX_EXTENSIONS target properties, or their variable equivalents to specify target defaults. Full details can be found here, but the short version goes something like this:

cmake_minimum_required(VERSION 3.1)
project(Example)

set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# ... Define targets, etc. as usual

CMake should then select the appropriate compiler flag for the requested C++ standard based on what the compiler supports, or error out if it doesn't support the requested standard.

It should also be noted that CMake may upgrade the target to use a later language standard than the one specified by its CXX_STANDARD target property. The use of compile feature requirements (as mentioned in @FlorianWolters answer) can raise the language standard requirement. In fact, CMake will always pick the stronger language requirement specified by either the CXX_STANDARD target property or the compile feature requirements set on the target. Note also that earlier versions of the CMake documentation did not accurately reflect the way CXX_EXTENSIONS interacts with compile features. With CMake 3.21 or earlier, CXX_EXTENSIONS would only take effect if CXX_STANDARD was also specified, for most common compilers (since they are specified together with the one compiler flag). From CMake 3.22 onward, CXX_EXTENSIONS is honoured whether CXX_STANDARD is set or not.

Centurion answered 16/3, 2017 at 18:45 Comment(2)
should I use all 3 properties together or only one of them is good enough?Brocade
I recommend setting all three. CMAKE_CXX_STANDARD on its own doesn't guarantee that you get that C++ standard. Without CMAKE_CXX_STANDARD_REQUIRED being set to true, CMake is allowed to fall back to an older C++ version if the one requested by CMAKE_CXX_STANDARD is not available. Setting CMAKE_CXX_EXTENSIONS to false ensures your project doesn't use non-standard C++, but just as importantly, it may affect which standard library your binaries link to. Policy CMP0128 is also relevant.Centurion
T
41

Modern CMake code should use the target_compile_features command to request a specific C++ standard. This can be specified as build requirement only (PRIVATE), usage requirement only (INTERFACE) or build and usage requirement (PUBLIC).

Example:

cmake_minimum_required(VERSION 3.9.4)

project(cpp-snippets)
add_executable(cpp-snippets "main.cpp")
target_compile_features(cpp-snippets PRIVATE cxx_std_17)

Refer to the section Requiring Language Standards in the official CMake documentation for cmake-compile-features to learn more.

Thadthaddaus answered 18/10, 2017 at 11:57 Comment(4)
What are "build requirements" as opposed to "usage requirements"?Tore
It should also be highlighted that support for the cxx_std_YY meta feature was also only added in CMake 3.8, whereas the CXX_STANDARD family of target properties have been around much longer and cxx_std_YY meta features alone don't control whether or not compiler extensions can be used.Centurion
@Tore As used in the answer, build requirements refer to things needed to build the target itself, whereas usage requirements are things that anything linking against the target will inherit as a requirement.Centurion
@Tore I should have used the terms Build Specification and Usage Requirements as used in the official documentation. Craig Scott already explained it correctly. Another great explaination can be found in the talk Effective CMake by Daniel Pfeifer. I highly recommend watching the linked video.Thadthaddaus
P
11

Check if the compiler supports the flags? Perhaps something like

include(CheckCXXCompilerFlag)

# Check for standard to use
check_cxx_compiler_flag(-std=c++17 HAVE_FLAG_STD_CXX17)
if(HAVE_FLAG_STD_CXX17)
    # Have -std=c++17, use it
else()
    check_cxx_compiler_flag(-std=c++1z HAVE_FLAG_STD_CXX1Z)
    if(HAVE_FLAG_STD_CXX1Z)
        # Have -std=c++1z, use it
    else()
        # And so on and on...
    endif()
endif()
Profanatory answered 16/3, 2017 at 13:4 Comment(3)
@Tore No speculation, I have been using this exact code for a couple of years without problem. With both GCC and Clang (which last time I tried did not support the -std=c++17 option).Profanatory
Why maintaining your own code, when CMake offers everything your want?Boyd
@Boyd Backward compatibility with older versions of CMake which doesn't have the flags and settings for newer C++ standards.Profanatory

© 2022 - 2024 — McMap. All rights reserved.