Jepessen
Jepessen

Reputation: 12435

Cannot set __cplusplus to C++17 standard with Visual Studio and CMake

I've a CMake project that's opened with Visual Studio 2019. I need c++17 features for my code, so I've set the corresponding flag in the CMakeLists.txt

cmake_minimum_required (VERSION 3.10.0)

project (datalog)

message (STATUS "Building project ${PROJECT_NAME}")

find_package(stxxl CONFIG REQUIRED)

include_directories (${CMAKE_SOURCE_DIR}/src)

set (PROJECT_SRC
  main.cpp
  )

add_executable (${PROJECT_NAME} ${PROJECT_SRC})
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17)

target_link_libraries(${PROJECT_NAME} stxxl)

When I build I obtain a lot of errors, because the library that I'm linking, stxxl, installed with vcpkg, has the following piece of code:

STXXL_BEGIN_NAMESPACE

template <class Type>
struct compat_unique_ptr {
#if __cplusplus >= 201103L && ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100) >= 40400)
    typedef std::unique_ptr<Type> result;
#else
    // auto_ptr is inherently broken and is deprecated by unique_ptr in c++0x
    typedef std::auto_ptr<Type> result;
#endif
};

STXXL_END_NAMESPACE

The problem is that __cplusplus has value 199711L instead of the correct value 201703L, so code tries to use auto_ptr that's removed in C++17 standard.

I've also tried to set the flag manually in Visual Studio, adding this section in the CmakeLists.txt

if(MSVC)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++17 /Zc:__cplusplus")
endif()

But nothing changes. I'm also using some C++17 features, so going back at the moment is not an option.

I can manually update the stxxl header file to get rid of the macro, but it's a workaround, and every developer should create the same fix. Instead, I'd like to know why I'm getting the error and how to set the macro to the correct value with Visual Studio and CMake. Do you have any suggestion?

Upvotes: 12

Views: 9629

Answers (2)

T S
T S

Reputation: 1895

As far as i can tell from this, you need to add /Zc:__cplusplus manually (at least for now).

Your CMakeList snippet worked for me, are you sure, that __cplusplus is actually set incorrectly and it's not just the __GNUC__ Macro, that's missing?

That said, I'd recommend to test if the version of VisualStudio is actually new enough:

# /Zc:__cplusplus is required to make __cplusplus accurate
# /Zc:__cplusplus is available starting with Visual Studio 2017 version 15.7
# (according to https://learn.microsoft.com/en-us/cpp/build/reference/zc-cplusplus)
# That version is equivalent to _MSC_VER==1914
# (according to https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=vs-2019)
# CMake's ${MSVC_VERSION} is equivalent to _MSC_VER
# (according to https://cmake.org/cmake/help/latest/variable/MSVC_VERSION.html#variable:MSVC_VERSION)
if ((MSVC) AND (MSVC_VERSION GREATER_EQUAL 1914))
    target_compile_options(stxxl INTERFACE "/Zc:__cplusplus")
endif()

Note: I used target_compile_options to add the necessary option to the interface of your library target. This way all targets, that depend on that library (via target_link_libraries) will be compiled with that flag, but targets, that don't need it (and might be incompatible with it) won't get it.

If you want to add the flag to one of your own targets, you can use target_compile_options(stxxl PUBLIC "/Zc:__cplusplus") (PUBLIC instead of INTERFACE). INTERFACE means the option is used when other targets depend on that target, PRIVATE means the option is used for the target itself and PUBLIC means both. (IMHO this artical explains it well.)


Apart from that, you shouldn't set the c++ standard version via compile flags, but instead using target_compile_features if the newer version is required and using
set_target_properties(your_target_name PROPERTIES CXX_STANDARD 17) if you just wish to use the newer version if it is available.

In your case the latter is probably sufficient as your codesnippet has a fallback if the c++ version is older.


If changing the code of stxxl (via pull request, etc.) is an option, the code could test _MSVC_LANG and _MSC_VER. This way it would work without requiring /Zc:__cplusplus.

Upvotes: 8

Lesley Lai
Lesley Lai

Reputation: 302

I think what you need is target_compile_definitions, where you can define arbitrary macros in CMake and pass to compiler.

Upvotes: -2

Related Questions