c++windowsc++17std-filesystemlong-path

How to properly handle windows paths with the long path prefix with std::filesystem::path


std::filesystem::path doesn't seem to be aware of the windows long path magic prefix. Is this per design or is there a mode/flag/compiler switch/3rd party library which can be used?

f.e.

I hoped that they would be C: and C:\ as well because \\?\ is not a path but just a tag used to work around the legacy windows 260 char path limit.

See a complete example on compilerexplorer.


Solution

  • As mentioned in the comments above, the source code of the Microsoft STL shows that the current behavior is intentional for UNC (Uniform Naming Convention) paths and that there is no "magic compiler switch" to change this. Moreover, it seems that UNC paths should not be used with std::filesystem, as implied by this github post. There is also an open issue that requests a change of behavior.

    Since the OP is ok with 3rd party libraries: boost::filesystem has special code on Windows builds for UNC paths and gives results that are closer to what is expected.

    Trying it out with the following code (adapted from the original post):

    #include <filesystem>
    #include <iostream>
    #include <string>
    #include <vector>
    
    #include <boost/filesystem.hpp>
    
    int main()
    {
      std::vector<std::string> tests = {
          R"(C:\temp\test.txt)",
          R"(\\?\C:\temp\test.txt)",
      };
    
      for (auto const & test : tests) {
        std::filesystem::path const p(test); // or boost::filesystem::path
        std::cout << std::endl << test << std::endl;
        std::cout << "root_name\t" << p.root_name().string() << std::endl;
        std::cout << "root_directory\t" << p.root_directory().string() << std::endl;
        std::cout << "root_path\t" << p.root_path().string() << std::endl;
        std::cout << "relative_path\t" << p.relative_path().string() << std::endl;
        std::cout << "parent_path\t" << p.parent_path().string() << std::endl;
        std::cout << "filename\t" << p.filename().string() << std::endl;
        std::cout << "stem\t" << p.stem().string() << std::endl;
        std::cout << "extension\t" << p.extension().string() << std::endl;
      }
    }
    

    For std::filesystem on MSVC I get

    C:\temp\test.txt
    root_name       C:
    root_directory  \
    root_path       C:\
    relative_path   temp\test.txt
    parent_path     C:\temp
    filename        test.txt
    stem    test
    extension       .txt
    
    \\?\C:\temp\test.txt
    root_name       \\?
    root_directory  \
    root_path       \\?\
    relative_path   C:\temp\test.txt
    parent_path     \\?\C:\temp
    filename        test.txt
    stem    test
    extension       .txt
    

    For boost::filesystem I get:

    C:\temp\test.txt
    root_name       C:
    root_directory  \
    root_path       C:\
    relative_path   temp\test.txt
    parent_path     C:\temp
    filename        test.txt
    stem    test
    extension       .txt
    
    \\?\C:\temp\test.txt
    root_name       \\?\C:
    root_directory  \
    root_path       \\?\C:\
    relative_path   temp\test.txt
    parent_path     \\?\C:\temp
    filename        test.txt
    stem    test
    extension       .txt