Is there an elegant way to allow either c++ <experimental/filesystem> or <filesystem> in CMake?

Viewed 482

I maintain a project written mostly in C++ that uses the <filesystem> feature of C++17. This code is intended to be run on Linux machines with either clang or gcc as the compiler. Recently, I learned that the current Ubuntu Long-Term Support (LTS) version uses gcc version 7.5 which is much older than the version 10.2 that I am using. The result is that on that version of gcc under Ubuntu LTS, it was still an experimental feature and so was in <experimental/filesystem>. To accomodate this, in each of the relevant source code files I have this:

#if HAS_FILESYSTEM
#include <filesystem>
namespace fs = std::filesystem;
#else
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
#endif

Inspired by this answer, https://stackoverflow.com/a/54290906/3191481 the CMakeFiles.txt file contains this.

if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
    try_compile(HAS_FILESYSTEM "${CMAKE_BINARY_DIR}/temp" 
        "${CMAKE_SOURCE_DIR}/compilertests/has_filesystem.cpp" 
        CMAKE_FLAGS -DCMAKE_CXX_STANDARD=17 -DCMAKE_CXX_STANDARD_REQUIRED=ON
        LINK_LIBRARIES stdc++fs)
    if (NOT HAS_FILESYSTEM)
        try_compile(HAS_EXPERIMENTAL_FILESYSTEM "${CMAKE_BINARY_DIR}/temp" 
            "${CMAKE_SOURCE_DIR}/compilertests/has_experimental_filesystem.cpp" 
            CMAKE_FLAGS -DCMAKE_CXX_STANDARD=17 -DCMAKE_CXX_STANDARD_REQUIRED=ON
            LINK_LIBRARIES stdc++fs)
    endif()
else()
    try_compile(HAS_FILESYSTEM "${CMAKE_BINARY_DIR}/temp" 
        "${CMAKE_SOURCE_DIR}/compilertests/has_filesystem.cpp")
    if (NOT HAS_FILESYSTEM)
        try_compile(HAS_EXPERIMENTAL_FILESYSTEM "${CMAKE_BINARY_DIR}/temp" 
            "${CMAKE_SOURCE_DIR}/compilertests/has_experimental_filesystem.cpp")
    endif()
endif()

if(HAS_FILESYSTEM)
    message(STATUS "Compiler has filesystem support")
    set(HAS_FILESYSTEM 1)
else()
    if(HAS_EXPERIMENTAL_FILESYSTEM)
        message(STATUS "Compiler has experimental filesystem support")
        set(HAS_FILESYSTEM 0)
    else()
        message(FATAL_ERROR "Compiler is missing filesystem capabilities")
    endif(HAS_EXPERIMENTAL_FILESYSTEM)
endif(HAS_FILESYSTEM)

The two files used by the try_compile lines above are these:

has_filesystem.cpp

#include <filesystem>
int main() {
    std::filesystem::path mypath{"../"};
}

has_experimental_filesystem.cpp

#include <experimental/filesystem>
int main() {
    std::experimental::filesystem::path mypath{"../"};
}

I also use a config file which is populated by CMake and contains this line:

#define HAS_FILESYSTEM @HAS_FILESYSTEM@

My questions

  1. This seems rather ungainly. Is there a better way to accomplish this?
  2. Because I use the HAS_FILESYSTEM value in a config.h.in file, I convert it to a number. Here again, is there a better way to do this?
0 Answers
Related