Как определить, использовать ли <filesystem>или <экспериментальная / файловая система>? - PullRequest
0 голосов
/ 19 ноября 2018

Есть ли способ определить, могу ли я использовать стандарт <filesystem> (который доступен на всех современных компиляторах C ++, поддерживающих C ++ 17) или <experimental/filesystem>, который используется более старыми компиляторами.(Например, g ++ 6.3, текущая стандартная версия Debian Stretch)

Знать, какой из них использовать, важно, поскольку первый использует std::filesystem::xxx, а второй std::experimental::filesystem::xxx.

Ответы [ 2 ]

0 голосов
/ 19 ноября 2018

Обычно я использую макросы для проверки функций для этой проблемы. В настоящее время я сталкиваюсь с этой проблемой, но я использовал __cpp_lib_filesystem вместе с ключевым словом using.

#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 и выше.

0 голосов
/ 19 ноября 2018

Я обычно создаю заголовок 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).
Если возникнет проблема с другим компилятором, дайте мне знать, и я тоже заставлю его работать.

...