std::filesystem's parent_path() is wrong for file in current directory?
Asked Answered
R

2

7

The traditional Unix shell utility, dirname, finds the name of the directory that contains the given file. This is useful if you want to find other sister files in the same directory. Here are a few examples:

$ dirname /some/dir/filename
/some/dir
$ dirname dirname/filename  
dirname
$ dirname filename
.

The new C++ std::filesystem's parent_path() gets this last one wrong (or so it appears):

#include <filesystem>
#include <iostream>
int main() {
    std::cout << "parent of filename: " <<
        std::filesystem::path("filename").parent_path() << '\n';
}

outputs:

parent of filename: ""

Returning an empty string instead of "." is not only different from the traditional dirname, it's also wrong when used in code which wants to tack on a "/..." on this parent to create the name of a sister files - this results in the wrong "/sister" instead of expected "./sister".

Is this a bug in std::filesystem, or deliberate behavior? Is the reason for this behavior documented somewhere?

Rosenarosenbaum answered 24/1 at 10:48 Comment(5)
Which compiler are you using?Herta
gcc and libstdc++ 13.2.1.Thankful
Concerning your question about documentation, see cppreference.com.Selfcontrol
Ulrich, I don't see anything relevant to my question in the short en.cppreference.com/w/cpp/filesystem/path/parent_path They don't cover this case in the text or example, let alone try to explain why it behaves this way which is what I wanted to know now (I already know how it behaves, but not why).Thankful
I see. Concerning the discussion that lead to this behaviour, you could try to find meeting notes from the ISO council that lead to this behaviour. Yes, it's not a fruitful endeavour...Selfcontrol
Z
5

I won't argue with you whether this is correct or not, but if you always use std::filesystem::operator/ instead of doing string manipulation, I don't see how you will run into the issue you mention in the question.

In particular, std::filesystem::path("filename").parent_path()/std::filesystem::path("filename") gives you just "filename", not "/filename". I believe this is how the API is supposed to be used.

Zirconia answered 24/1 at 11:53 Comment(2)
The RHS of operator/ can be a simple string literal, no need to create a path instance from it first: en.cppreference.com/w/cpp/filesystem/path/append.Selfcontrol
Yes. but in more complex cases you want to a more complex path in the end and then that should definitely be a path object, not some string containing slashes.Zirconia
D
3

Using std::filesystem::path::parent_path, the result is based on the value passed to std::filesystem::path. This function is only meant to make some simple parsing and not to resolve any path.

Globaly std::filesystem::path is, as the documentation describe it, a representation of a path. If you wan't to have the same behavior as dirname, you need to look for other function available in std::filesystem namespace and build your own custom tool.

Edit:

The use of "simple" to describe the parsing strategy, mean it doesn't verify the actual value of what is parse, as the description of the function parent_path:

If has_relative_path() returns false, the result is a copy of *this. Otherwise, the result is 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.

Even if it's not explicit enought (for me at least), after a few tests, the produces one fewer element in its iteration sentence translate to: produces a path to the previous directory-separators without any path resolution.

Dumond answered 24/1 at 11:2 Comment(5)
dirname also doesn't do any sophisticated filesystem lookups and does only simple textual parsing. E.g., dirname /a/b/../c/x returns /a/b/../c and doesn't try to "canonize" it in any way. It's just the behavior for a file in the current directory which is different - and looks to me non-useful (it broke my application), so I was wondering if it's intentional and if so why.Thankful
I update the respond, with a more accurate parsing strategy explanation. For the part of why is it made this way, it's only a guess, but I think this is to have some flexibility in the resolution from the other function in the std::filesystem namespace.Dumond
DipStax thanks for the edit. I guess that description really fits what the function does today, but doesn't explain why it does it: If the given path has a single component, a file in the current directory: "filename", then the output will be a path with zero components. Right. But why is this useful? This is not the true parent directory. The true parent directory is ".", which also has one component, not zero.Thankful
The return of an empty string is certainly due of . being actually a file that contain the information about the actual directory. A directory is always represented by a /, for example if I use /.: this is a correct path because / represent the actual directory "position" and . is only the information file of the directory /.Dumond
So to keep the produces a path to the previous directory-separators without any path resolution, you can't give an other file as result (in the case the entry is "filename").Dumond

© 2022 - 2024 — McMap. All rights reserved.