3

I'm using C++17's std::filesystem::absolute to convert relative paths to absolute ones. I noticed that on Windows (MSVC), this function seems to resolve . and .. components in the path, but on Linux (GCC/libstdc++), these components remain in the result.

For example, if my current directory is /home/user (or C:\Users on Windows):

std::filesystem::path rel = "foo/./bar/child/..";
auto abs = std::filesystem::absolute(rel);

On Windows, I get: C:\Users\foo\bar On Linux, I get: /home/user/foo/./bar/child/..

Is this platform-dependent behavior expected according to the C++ standard?

5
  • 1
    filesystem paths are by definition platform-dependent. The Windows UNC path //SOMESERVER/SOMESHARE/... should also not be flattened to /SOMESERVER/SOMESHARE as on Linux, because it completely changes the semantics. Commented Apr 29 at 8:49
  • 2
    You should check out std::filesystem::canonical if you want to make paths as simple as possible. Note that it expects the path to exist, which is not the case for absolute. Commented Apr 29 at 8:53
  • Whereas, when following link, foo/../bar might not be equivalent to bar, I don't see how foo/./bar can lead to different path than foo/bar... Commented Apr 29 at 8:59
  • For fun, on Windows you should check how std::filesystem interacts with hard links, junctions, and symbolic links. Commented Apr 29 at 13:20
  • @AtulRawat Your question title says that .. is not resolved on Posix, but your example shows that it is. Commented Apr 29 at 17:18

3 Answers 3

7

Most probably as a solution you should use function lexically_normal() which will strip any relative parts of the path from absolute path.

#include "catch2/catch_all.hpp"

#include <filesystem>

namespace fs = std::filesystem;

TEST_CASE("filesystem") {
    auto [relative, absolute] = GENERATE(table<fs::path, fs::path>({
        { ".", "/app/" },
        { "./foo/../bar", "/app/bar" },
        { "./foo/././bar", "/app/foo/bar" },
    }));
    CAPTURE(relative, absolute);
    // CHECK(fs::absolute(relative) == absolute);
    CHECK(fs::absolute(relative).lexically_normal() == absolute);
}

https://godbolt.org/z/nWEhxfETK

Sign up to request clarification or add additional context in comments.

Comments

2

Yes, it's platform-dependent (it is more or less impossible to present a unified filesystem interface for all platforms) and POSIX defines an absolute path as "[a] pathname beginning with a single or more than two <slash> characters".

See fs.op.absolute, note 7:

For POSIX-based operating systems, absolute(p) is simply current_path()/p. For Windows-based operating systems, absolute might have the same semantics as GetFullPathNameW.

(And note the "might have".)

Comments

1
  • A path leads you to a destination (a file).

  • A relative path leads you to a destination starting from your current location.

  • An absolute path leads you to a destination starting from a fixed location.

  • A normalized path leads you to a destination without needless backtracking (x/../) or pausing (/./ or ////) and with all slashes pointing the "right" way (for your platform).

  • A canonical path avoids backtracking, pauses, and also shortcuts (symbolic links).


Normalization is orthogonal to relative/absolute. The former deals with how the path proceeds, while the latter deal with how the path starts. Both relative and absolute paths can be normalized, and neither requires it. Note, though, that to make things more complicated, std::filesystem::canonical returns a canonical absolute path, even though "absolute" is not in the function name. Special case, not the norm.

std::filesystem::absolute promises an absolute path to the specified destination, nothing more. This makes sense, given the principle that you do not pay for what you do not need. If you need the result normalized, you can always invoke lexically_normal() on the returned value. If you are using the same parameter multiple times (from different starting directories), it might be better to normalize the parameter in advance, although there is no guarantee that the leading .. will be resolved by absolute().

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.