Determining the compiler default C++ standard

Hi,

This question is related to setting c++ standard from target_compile_option goes for higher standard - #8 by adaldev.

Investigating on the question of the actual C++ dialect used when requesting on with target_compile_features I discovered that my CMake version set 2 variables:

CMAKE_CXX_STANDARD_COMPUTED_DEFAULT
CMAKE_CXX_STANDARD_DEFAULT

They actually seemed to correspond to the requested compiler default standard and CMake behavior seems to replace my target_compile_features request by the compiler default if this one is higher than my demand.

The reason why has been discussed in the provided link but now I’m wondering if these variable can be safely used to deduce and report the actual version used (which does not appear in any other variable). AFAIK, they are not cached variables, and no cached variable contains the desired information either.

Regards,
A.

As I said there, it’s a generator-time determination of what the actual standard in use is. The only 100% reliable way to detect any of this is with the __cplusplus preprocessor symbol.

Thanks,

I’d like to be sure to understand.
CMAKE_CXX_STANDARD_DEFAULT is set at configuration time but actual standard used (for instance through an added -std option) is determined at generation time?

Is there a rule that, if user is requesting some standard, the chosen option at generation time is the greater of the user-requested and the CMAKE_CXX_STANDARD_DEFAULT?
Is it a behavior that can change in time or that depends on the generator?

IMHO, it’s a “bug” that this behavior is not explicitly documented (but, as discussed in another thread, maybe I’m over-zealous and the only thing that matters for coder is to have, at least, the requested standard).

Regards
PS though C++ tries to be legacy-compliant it’s not always the case. I’ve got code that broke because some functions became constexpr and it changed some specialization choices in library using metaprogramming, leading to wrong behavior.
It took me time to track down the issue and, in the meantime, it was a real issue to have my builds updated to C++17, while only C++14 was working.

There was yet another thread where enforcing a C++ maximum version within the ,cpp source files was discussed.

#if __cplusplus > 
#error 
#endif

Another way to do this might be to check each target property cxx_standard If the source C++ files cannot be modified due to management decision.

Another way would be to have cmake generate and then a separate checker script parse compile_commands.json. although that does not account for the default C++ standard of particular compiler versions.

Hmm, I could not find where the merge logic of “use newest compile feature” is documented. @brad.king? We could use a few extra cross-references to where it does (will?) live from COMPILE_FEATURES, target_compile_features, and the <LANG>_STANDARD variable docs.

CMAKE_CXX_STANDARD[_COMPUTED]_DEFAULT are implementation details that are not documented or meant for public use.

where the merge logic of “use newest compile feature” is documented

I don’t think we document any specific algorithm for selecting a standard level. The documentation of each cxx_std_## feature says it means only that the “Compiler mode is at least C++##”. That document also says “If the compiler’s default standard level is at least that of the requested feature, CMake may omit the -std= flag.”

Thanks for all the details.

I note also that I should not use CMAKE_CXX_STANDARD[_COMPUTED]_DEFAULT.

Thus it leaves me with my issue where I needed (at least temporarily) an exact C++ dialect. Do you feel it can be a motivation for opening an issue (feature request)?

Regards

Use CMAKE_CXX_STANDARD/CXX_STANDARD and CMAKE_CXX_STANDARD_REQUIRED/CXX_STANDARD_REQUIRED for that. The specified stanard should be used unless some feature passed to target_compile_features requires a higher one.

I thought even CXX_STANDARD still ended up being part of the same logic that ensured that at least that version was used, not that it should use that exact version. In other words, I don’t think CXX_STANDARD guarantees you’ll get that exact standard either, even if there are no target_compile_features() that ask for something higher. But I haven’t checked the code lately to reconfirm my understanding (it’s been a while since I’ve looked at that logic).

The cxx_std_## features are for minimum standard levels. CXX_STANDARD will be honored even if it is lower than the compiler’s default, and an explicit -std= flag will be used to achieve it if necessary.

1 Like

Hi,

I’ve just made a test. I’ve replaced

target_compile_features(${TARGET_NAME} PUBLIC cxx_std_14)

by

set(CXX_STANDARD C++14)

I’ve cleaned the cache and in both cases, -std=c++17 is added to my gcc command line.

Regards
A.

Unless the project code does something with that, it is not meaningful to CMake. To request C++14 via the property/variable use either:

set_property(TARGET ${TARGET_NAME} PROPERTY CXX_STANDARD 14)

after creating the target, or

set(CMAKE_CXX_STANDARD 14)

before creating the target.

My bad. This way it seems to work.

In order to sum up the thread:

  • there’s no “official” way to check the standard that will be actually chosen when using target_compile_features
  • you can force a standard with set_property(TARGET ${TARGET_NAME} PROPERTY CXX_STANDARD <value>)

Thanks again, regards
PS I’ll mark this comment as answer as it is merging all the provided information

Is CMAKE_CXX_STANDARD forcing flag a policy-enabled behavior? I can’t make it work when building protobuf. I don’t have minimal reproducer yet, but here are pieces I have found so far:

MSVC compiler’s CMakeCXXCompiler.cmake has:

set(CMAKE_CXX_STANDARD_COMPUTED_DEFAULT "14")
set(CMAKE_CXX_EXTENSIONS_COMPUTED_DEFAULT "OFF")

configure is called with -DCMAKE_CXX_STANDARD=14 and CMakeCache.txt contains following:

CMAKE_CXX_STANDARD:UNINITIALIZED=14

target configuration has following snippet:

    target_compile_features("${target}" PUBLIC cxx_std_14)

and unless I change it to cxx_std_17 I see no /std:c++14 flags passed to cl.exe

My CMake is 3.30.3, but project CMakeLists.txt is cmake_minimum_required(VERSION 3.10...3.26).

Done few more experiments:

  1. remove cxx_std_14 + CMAKE_CXX_STANDARD=14: NO FLAG
  2. remove cxx_std_14 + CMAKE_CXX_STANDARD=17: flag /std:c++17 is added
  3. keep cxx_std_14 + CMAKE_CXX_STANDARD=17: flag /std:c++17 is added
  4. (as mentioned in post) updating to cxx_std_17 and not passing CMAKE_CXX_STANDARD: flag /std:c++17 is passed

It seems there is no way to build with /std:c++14 if compiler detected STANDARD_COMPUTED_DEFAULT=14 :-/

maybe important: target depends on imported target which has following:

set_target_properties(absl::core_headers PROPERTIES
  INTERFACE_COMPILE_FEATURES "cxx_std_14"

cl does not model standards below C++14, so it requires no flag to satisfy CMAKE_CXX_STANDARD=14 or cxx_std_14.

I have a problem with third-party vendor toolchain where CMake incorrectly detects (probably self-reported by toolchain/compiler) C+14 as default standard and as a result some code can’t be compiled: it would if -std=C+14 was passed, but it didn’t get passed.

I started to experiment with CMAKE_CXX_STANDARD on MSVC as it is something I can potentially build reproducer with to report here.

Reading this thread, I had an impression that CMAKE_CXX_STANDARD=14 property should force adding std=c++14 flag even if compiler’s default standard is C++14, but it seems I misunderstood.

Looks like my only option is to either force C++17 or fix compiler detection.

As mentioned above, did you try set_property(TARGET ${TARGET_NAME} PROPERTY CXX_STANDARD <value>) to force C++ standard?

A.

I did try cmake -DCMAKE_CXX_STANDARD=14 which adds CXX_STANDARD property to all non-imported targets. Do you have reason to believe that explicit property will have different effect?

From here: