Search code examples
c++std-filesystem

How can a std::filesystem root path have itself as a parent?


This program:

#include <iostream>
#include <filesystem>

int main()
{
    std::filesystem::path p1("c:\\");
    std::filesystem::path p2("c:/");
    if (p1.has_parent_path())
        std::cout << "Parent path of " << p1 << " is " << p1.parent_path() << std::endl;
    if (p2.has_parent_path())
        std::cout << "Parent path of " << p2 << " is " << p2.parent_path() << std::endl;
}

Produces this output:

Parent path of "c:\\" is "c:\\"
Parent path of "c:/" is "c:/"

(EDIT: There was confusion about my use of forward slash so I updated this code to show the same thing happens regardless of which path separators you use on Windows)

This makes no sense to me. How can a directory be the parent of itself? What is the point of even having a "has_parent" function if it's never going to return false?

Most importantly: If I am writing code that recursively searches up a directory tree looking for a file, what is the best/most reliable way to detect that I've hit the root folder and should stop?

(I'm using Visual Studio 2019 in C++17 language mode, if that matters)


Solution

  • Actually there is no output in the godbolt example, because the test is run on GCC on a posix environment, where "C:\\" is not a root directory, so it is seen as a strange directory/file name and its parent is empty, but with the correct posix root path "/" it would have given the output, as the OP is observing. So no, parent_path() of a root directory ("/" or "C:\\") is not empty, so has_parent_path() is true on all implementations of std::filesystem I know of.

    The actual standard says: "Returns: *this if has_relative_path() is false, otherwise a path whose generic format pathname is the longest prefix of the generic format pathname of *this that produces one fewer element in its iteration." and relative_path() is everything after the root_path(), so there is no relative_path() in this case, so parent_path() returns *this and has_parent_path() returns true, as in the example of the OP.

    My guess with the question of why the standard chose this behaviour is, that this is what cd .. does on every OS, if you are already at the root, you stay at the same root.

    Indeed one possible way of detecting the end is: (!p.has_parent_path() || !p.has_relative_path()) or maybe even !p.has_relative_path(), depending on if you want to end with an empty path if it was rootless.