Я обычно создаю заголовок 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::filesystem
до std::filesystem
, если используются экспериментальные заголовки.
Это означает, что вы можете просто включить этот заголовок вместо <filesystem>
, использовать std::filesystem::xxx
и пользоваться поддержкой более старых компиляторов.
Несколько примечаний к деталям этого фрагмента:
__cpp_lib_filesystem
и __cpp_lib_experimental_filesystem
Это Макросы тестирования функций . Они должны быть доступны, когда доступны соответствующие заголовки. Но VisualStudio 2015 (и ниже) не поддерживает их. Так что остальное - просто убедиться, что мы можем сделать точную оценку, а не полагаться на ненадежные макросы.
__has_include()
Хотя в большинстве компиляторов этот макрос встроен, гарантии нет, так как его нет в стандарте. Мой фрагмент проверяет его существование перед использованием. И в случае, если он не существует, мы предполагаем, что мы должны использовать экспериментальную версию для обеспечения максимальной совместимости.
defined(_MSC_VER) && !(defined(_HAS_CXX17) && _HAS_CXX17)
В некоторых версиях VisualStudio (а именно в 2015 году) реализована лишь половина аромата C ++ 17. И возможно, что заголовок <filesystem>
существует, но std::filesystem
нет. Эта строка проверяет этот случай и вместо этого использует экспериментальную версию.
#error ...
Если проверка заголовка доступна, и мы не можем найти ни один заголовок, мы просто выводим хорошую ошибку, поскольку мы ничего не можем сделать.
INCLUDE_STD_FILESYSTEM_EXPERIMENTAL
Вы даже получаете марку, которая дает вам знать, какая версия используется, чтобы вы могли написать собственные операторы предварительной обработки, которые касаются различий между версиями.
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).
Если возникнет проблема с другим компилятором, дайте мне знать, и я тоже заставлю его работать.