如何判断是使用<filesystem>还是<experimental/filesystem>?
How to determine whether to use <filesystem> or <experimental/filesystem>?
有没有办法确定我是否可以使用标准 <filesystem>
(所有支持 C++17 的现代 C++ 编译器都可用)或旧编译器使用的 <experimental/filesystem>
. (例如 g++ 6.3,这是 Debian Stretch 上的当前标准版本)
知道使用哪个很重要,因为第一个使用 std::filesystem::xxx
而后者 std::experimental::filesystem::xxx
.
我通常会创建一个 header 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
如果使用实验性 header,它甚至会为 std::experimental::filesystem
到 std::filesystem
创建一个别名。
这意味着您可以简单地包含此 header 代替 <filesystem>
,使用 std::filesystem::xxx
并享受旧编译器的支持。
有关此代码段细节的几点说明:
__cpp_lib_filesystem
和 __cpp_lib_experimental_filesystem
这些是 Feature Testing Macros。当相应的 header 可用时,它们应该可用。但 VisualStudio 2015(及以下版本)不支持它们。所以剩下的只是为了确保我们能够做出准确的评估,而不是依赖不可靠的宏。
__has_include()
虽然大多数编译器确实内置了该宏,但不能保证,因为它不在标准中。我的代码片段在使用之前检查它是否存在。如果它不存在,我们假设我们必须使用实验版本来提供最大的兼容性。
defined(_MSC_VER) && !(defined(_HAS_CXX17) && _HAS_CXX17)
某些版本的 VisualStudio(即 2015)只有半生不熟的 C++17 实现。 <filesystem>
header 可能存在,但 std::filesystem
不存在。此行检查该情况并改用实验版本。
#error ...
如果 header 检查可用,但我们找不到 header 我们只是打印一个很好的错误,因为我们无能为力。
INCLUDE_STD_FILESYSTEM_EXPERIMENTAL
您甚至可以获得一个宏,让您知道正在使用哪个版本,这样您就可以编写自己的预处理器语句来处理版本之间的差异。
namespace filesystem = experimental::filesystem;
这个别名定义只是为了确保你有std::filesystem
,假设你的编译器让你这样做(我还没有看到一个不允许这样做的)。
根据标准,在 std
命名空间中定义的任何内容都是未定义的行为。因此,如果您的编译器、concience、colleguages、代码标准或任何抱怨,只需在上部块中定义 namespace fs = std::experimental::filesystem;
并在下部块中定义 namespace fs = std::filesystem;
。 (只是为了确定,如果你这样做,删除 namespace std {
东西)
P.S.: 我创建了答案和这个问题,因为我花了很多时间对没有 <filesystem>
header 的旧编译器感到沮丧。在使用多个编译器及其版本在多个平台上进行大量研究和测试后,我设法提出了这个通用解决方案。我已经使用 VisualStudio、g++ 和 clang 对其进行了测试(仅使用实际至少具有对 C++17 的实验性支持的版本)。
如果其他编译器有问题,请告诉我,我也会解决它。
对于此类问题,我通常会大量使用 feature test macros。我目前正面临这个确切的问题,但我使用了 __cpp_lib_filesystem
和 using
关键字。
// since C++ 20
#include <version>
#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 上使用它,遗憾的是没有旧版工作室可供测试,但它适用于 15.7 及更高版本。
有没有办法确定我是否可以使用标准 <filesystem>
(所有支持 C++17 的现代 C++ 编译器都可用)或旧编译器使用的 <experimental/filesystem>
. (例如 g++ 6.3,这是 Debian Stretch 上的当前标准版本)
知道使用哪个很重要,因为第一个使用 std::filesystem::xxx
而后者 std::experimental::filesystem::xxx
.
我通常会创建一个 header 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
如果使用实验性 header,它甚至会为 std::experimental::filesystem
到 std::filesystem
创建一个别名。
这意味着您可以简单地包含此 header 代替 <filesystem>
,使用 std::filesystem::xxx
并享受旧编译器的支持。
有关此代码段细节的几点说明:
__cpp_lib_filesystem
和__cpp_lib_experimental_filesystem
这些是 Feature Testing Macros。当相应的 header 可用时,它们应该可用。但 VisualStudio 2015(及以下版本)不支持它们。所以剩下的只是为了确保我们能够做出准确的评估,而不是依赖不可靠的宏。__has_include()
虽然大多数编译器确实内置了该宏,但不能保证,因为它不在标准中。我的代码片段在使用之前检查它是否存在。如果它不存在,我们假设我们必须使用实验版本来提供最大的兼容性。defined(_MSC_VER) && !(defined(_HAS_CXX17) && _HAS_CXX17)
某些版本的 VisualStudio(即 2015)只有半生不熟的 C++17 实现。<filesystem>
header 可能存在,但std::filesystem
不存在。此行检查该情况并改用实验版本。#error ...
如果 header 检查可用,但我们找不到 header 我们只是打印一个很好的错误,因为我们无能为力。INCLUDE_STD_FILESYSTEM_EXPERIMENTAL
您甚至可以获得一个宏,让您知道正在使用哪个版本,这样您就可以编写自己的预处理器语句来处理版本之间的差异。namespace filesystem = experimental::filesystem;
这个别名定义只是为了确保你有std::filesystem
,假设你的编译器让你这样做(我还没有看到一个不允许这样做的)。
根据标准,在std
命名空间中定义的任何内容都是未定义的行为。因此,如果您的编译器、concience、colleguages、代码标准或任何抱怨,只需在上部块中定义namespace fs = std::experimental::filesystem;
并在下部块中定义namespace fs = std::filesystem;
。 (只是为了确定,如果你这样做,删除namespace std {
东西)
P.S.: 我创建了答案和这个问题,因为我花了很多时间对没有 <filesystem>
header 的旧编译器感到沮丧。在使用多个编译器及其版本在多个平台上进行大量研究和测试后,我设法提出了这个通用解决方案。我已经使用 VisualStudio、g++ 和 clang 对其进行了测试(仅使用实际至少具有对 C++17 的实验性支持的版本)。
如果其他编译器有问题,请告诉我,我也会解决它。
对于此类问题,我通常会大量使用 feature test macros。我目前正面临这个确切的问题,但我使用了 __cpp_lib_filesystem
和 using
关键字。
// since C++ 20
#include <version>
#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 上使用它,遗憾的是没有旧版工作室可供测试,但它适用于 15.7 及更高版本。