Is there a way to determine whether I can use the standard <filesystem>
(which is available on all modern C++ compilers that support C++17) or <experimental/filesystem>
which is used by older compilers. (For example g++ 6.3, which is the current standard version on Debian Stretch)
Knowing which one to use is imporant because the first uses std::filesystem::xxx
and the latter std::experimental::filesystem::xxx
.
Filesystem library provides facilities to manipulate files and directories, and the paths that identify them. The features of the library include: A modern C++ interface, highly compatible with the C++ standard library.
The Filesystem library provides facilities for performing operations on file systems and their components, such as paths, regular files, and directories. The filesystem library was originally developed as boost.
I typically create a header filesystem.hpp
with the following content:
// 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
It even creates an alias for std::experimental::filesystem
to std::filesystem
if the experimental headers are used.
Which means you can simply include this header in place of <filesystem>
, use std::filesystem::xxx
and enjoy support from older compilers too.
A few notes on the details of this snippet:
__cpp_lib_filesystem
and __cpp_lib_experimental_filesystem
__has_include()
defined(_MSC_VER) && !(defined(_HAS_CXX17) && _HAS_CXX17)
<filesystem>
header exists, but std::filesystem
doesn't. This line checks for that case and uses the experimental version instead.#error ...
INCLUDE_STD_FILESYSTEM_EXPERIMENTAL
namespace filesystem = experimental::filesystem;
std::filesystem
, assuming your compiler let's you do it (I haven't seen a single one that doesn't allow that).std
namespace is undefined behavior. So if your compiler, concience, colleguages, code standard or whatever complains, just define namespace fs = std::experimental::filesystem;
in the upper block and namespace fs = std::filesystem;
in the lower. (Just to be sure, if you do that, remove the namespace std {
stuff)P.S.: I created the answer and this question, because I spent an awful lot of time getting frustrated with older compilers not having the <filesystem>
header. After a fair amount of research and testing on multiple platforms with multiple compilers and versions of them, I managed to come up with this universal solution. I have tested it with VisualStudio, g++ and clang (Only with versions that actually do have at least experimental support for C++17).
Should there be an issue with another compiler, let me know and I'll make it work for it too.
I typically use the feature test macros a lot for this type of problem. I am currently faced with this exact problem but I have used __cpp_lib_filesystem
along with the using
keyword.
// 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
I am using this on gcc-6 and up as well as clang-6, sadly no older copy of studio to test against but it works on 15.7 and up.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With