Joe
Joe

Reputation: 6816

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)

Upvotes: 4

Views: 3236

Answers (2)

Gulrak
Gulrak

Reputation: 816

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.

Upvotes: 4

NutCracker
NutCracker

Reputation: 12273

Function bool has_parent_path() const; checks whether the path returned from the path parent_path() const; function is empty.

Furthermore, for the path parent_path() const; function, the standard says:

Returns the root directory of the generic-format path. If the path (in generic format) does not include root directory, returns path().

On Linux systems, where the root path is /, and on Windows systems, where the root path is C:\\, parent_path() function would return empty string so, consequently, has_parent_path() function would return false.

Check this code from here:

#include <iostream>
#include <experimental/filesystem>

namespace fs = std::experimental::filesystem;

int main() {
    for (fs::path p : {"/var/tmp/example.txt", "/", "C:\\", "/var/tmp/."}) {
        std::cout << "The parent path of " << p
                  << " is " << p.parent_path() << '\n';
        std::cout << "Has parent path: " << p.has_parent_path() << '\n';
    }

    return 0;
}

Pay attention to spelling out the Windows root directory. It is not c:/ but C:\\.

Upvotes: 0

Related Questions