如何确定使用<filesystem>还是<experimental filesystem =“”>?

时间:2018-11-18 21:15:30

标签: c++ filesystems c++17

有没有一种方法可以确定我是否可以使用标准的<filesystem>(在所有支持C ++ 17的现代C ++编译器中都可以使用)还是<experimental/filesystem>(由较早的编译器使用)。 (例如g ++ 6.3,这是Debian Stretch上的当前标准版本)

知道要使用哪个是重要的,因为前者使用std::filesystem::xxx,后者使用std::experimental::filesystem::xxx

2 个答案:

答案 0 :(得分:8)

我通常创建带有以下内容的标头filesystem.hpp

// We haven't checked which filesystem to include yet
#ifndef INCLUDE_STD_FILESYSTEM_EXPERIMENTAL

// Check for feature test macro for <filesystem>
#   if defined(__cpp_lib_filesystem)
#       define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 0

// Check for feature test macro for <experimental/filesystem>
#   elif defined(__cpp_lib_experimental_filesystem)
#       define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 1

// We can't check if headers exist...
// Let's assume experimental to be safe
#   elif !defined(__has_include)
#       define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 1

// Check if the header "<filesystem>" exists
#   elif __has_include(<filesystem>)

// If we're compiling on Visual Studio and are not compiling with C++17, we need to use experimental
#       ifdef _MSC_VER

// Check and include header that defines "_HAS_CXX17"
#           if __has_include(<yvals_core.h>)
#               include <yvals_core.h>

// Check for enabled C++17 support
#               if defined(_HAS_CXX17) && _HAS_CXX17
// We're using C++17, so let's use the normal version
#                   define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 0
#               endif
#           endif

// If the marco isn't defined yet, that means any of the other VS specific checks failed, so we need to use experimental
#           ifndef INCLUDE_STD_FILESYSTEM_EXPERIMENTAL
#               define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 1
#           endif

// Not on Visual Studio. Let's use the normal version
#       else // #ifdef _MSC_VER
#           define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 0
#       endif

// Check if the header "<filesystem>" exists
#   elif __has_include(<experimental/filesystem>)
#       define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 1

// Fail if neither header is available with a nice error message
#   else
#       error Could not find system header "<filesystem>" or "<experimental/filesystem>"
#   endif

// We priously determined that we need the exprimental version
#   if INCLUDE_STD_FILESYSTEM_EXPERIMENTAL
// Include it
#       include <experimental/filesystem>

// We need the alias from std::experimental::filesystem to std::filesystem
namespace std {
    namespace filesystem = experimental::filesystem;
}

// We have a decent compiler and can use the normal version
#   else
// Include it
#       include <filesystem>
#   endif

#endif // #ifndef INCLUDE_STD_FILESYSTEM_EXPERIMENTAL

如果使用实验性标头,它甚至可以为std::experimental::filesystemstd::filesystem创建别名。
这意味着您可以简单地将此标头包含在<filesystem>的位置,使用std::filesystem::xxx并享受旧编译器的支持。

有关此代码段详细信息的几点说明:

  • __cpp_lib_filesystem__cpp_lib_experimental_filesystem
    这些是Feature Testing Macros。当相应的标头可用时,它们应该可用。但是VisualStudio 2015(及更低版本)不支持它们。因此,剩下的只是确保我们可以进行准确的评估,而不是依靠不可靠的宏。
  • __has_include()
    尽管大多数编译器的确内置了该宏,但没有保证,因为它不在标准中。我的代码段在使用前先检查是否存在。如果不存在,我们假设我们必须使用实验版本以提供最大的兼容性。
  • defined(_MSC_VER) && !(defined(_HAS_CXX17) && _HAS_CXX17)
    Visual Studio的某些版本(即2015年)仅实现了C ++ 17的一半实现。并且<filesystem>标头可能存在,但std::filesystem不存在。该行将检查这种情况,并使用实验版本。
  • #error ...
    如果可以使用标头检查并且找不到任何标头,那么我们只会打印一个错误,因为我们无能为力。
  • INCLUDE_STD_FILESYSTEM_EXPERIMENTAL
    您甚至会得到一个marco,让您知道正在使用哪个版本,因此您可以编写自己的预处理程序语句来处理版本之间的差异。
  • namespace filesystem = experimental::filesystem;
    此别名定义只是为了使您确信std::filesystem,假设您的编译器让您这么做(我没有看到一个不允许这样做的人)。
    根据标准,在std命名空间中定义的任何内容都是未定义的行为。因此,如果您的编译器,便利性,语言,代码标准或任何有问题的地方,只需在上方的块中定义namespace fs = std::experimental::filesystem;,在下方的块中定义namespace fs = std::filesystem;。 (只需确定,如果这样做,请删除namespace std {内容)

P.S .:我创建了答案和这个问题,因为我花了很多时间对没有<filesystem>头的较旧的编译器感到沮丧。在具有多个编译器和版本的多个平台上进行了大量研究和测试之后,我设法提出了这个通用解决方案。我已经用VisualStudio,g ++和clang对其进行了测试(仅在实际上至少具有对C ++ 17的实验支持的版本中)。
如果其他编译器有问题,请告诉我,我也将使其解决问题。

答案 1 :(得分:2)

对于这种类型的问题,我通常会大量使用feature test macros。我目前面临这个确切的问题,但是我将__cpp_lib_filesystemusing关键字一起使用了。

#ifdef __cpp_lib_filesystem
    #include <filesystem>
    using fs = std::filesystem;
#elif __cpp_lib_experimental_filesystem
    #include <experimental/filesystem>
    using fs = std::experimental::filesystem;
#else
    #error "no filesystem support ='("
#endif

我正在gcc-6和更高版本以及clang-6上使用它,遗憾的是没有旧版本的Studio可以用来测试,但是它可以在15.7和更高版本上使用。