std::filesy...y_canonical(3)

πŸ“¦ version 2024.07.05, in nixpkgs unstable, opensuse-leap 16.0

πŸ’– NAME πŸ’–

okay so this is the <cyan>std::filesystem::canonical</cyan> / <cyan>std::filesystem::weakly_canonical</cyan> page from the <pink>C++ Standard Libary</pink> manual (yes, that's how the manual name is spelled in the header, bestie β€” we keep it πŸ’…). The page title is std::filesy...y_canonical(3), and the NAME line is basically std::filesystem::canonical,std::filesystem::weakly_canonical β€” std::filesystem::canonical,std::filesystem::weakly_canonical. Slay, we're covering both. ✨


✨ Synopsis ✨

Defined in header <filesystem> β€” yes, that one, no cap 🫢

path canonical( const std::filesystem::path& p );        (1) (since C++17)
path canonical( const std::filesystem::path& p,          (2) (since C++17)
                std::error_code& ec );
path weakly_canonical( const std::filesystem::path& p ); (3) (since C++17)
path weakly_canonical( const std::filesystem::path& p,   (4) (since C++17)
                       std::error_code& ec );
  • 1,2) Converts path p to a canonical absolute path, i.e. an absolute path that has no dot, dot-dot elements or symbolic links in its generic format representation. If p is not an absolute path, the function behaves as if it is first made absolute by std::filesystem::absolute(p). The path p must exist. πŸ’€
  • 3,4) Returns a path composed by operator/= from the result of calling canonical() with a path argument composed of the leading elements of p that exist (as determined by status(p) or status(p, ec)), if any, followed by the elements of p that do not exist. The resulting path is in normal form. 🌸
bestie, canonical() requiring p to exist is not a vibe check you can skip β€” it's literally in the description. weakly_canonical() is the one that handles the non-existent tail. πŸŽ€

🫢 Parameters 🫢

  • p β€” a path which may be absolute or relative; for canonical it must be an existing path
  • ec β€” error code to store error status to

🌸 Return value 🌸

  • 1,2) An absolute path that resolves to the same file as std::filesystem::absolute(p).
  • 3,4) A normal path of the form canonical(x)/y, where x is a path composed of the longest leading sequence of elements in p that exist, and y is a path composed of the remaining trailing non-existent elements of p.

πŸ’€ Exceptions πŸ’€

Any overload not marked noexcept may throw std::bad_alloc if memory allocation fails. 😭

  • 1,3) Throws std::filesystem::filesystem_error on underlying OS API errors, constructed with p as the first path argument and the OS error code as the error code argument.
  • 2,4) Sets a std::error_code& parameter to the OS API error code if an OS API call fails, and executes ec.clear() if no errors occur.

🧚 Notes 🧚

  • The function canonical() is modeled after the POSIX realpath. iconic behavior, honestly. πŸ‘‘
  • The function weakly_canonical() was introduced to simplify operational semantics of relative().

πŸ“ Example πŸ“

// Run this code

#include <filesystem>
#include <iostream>

int main()
{
    /* set up sandbox directories:
     a
     └── b
         β”œβ”€β”€ c1
         β”‚   └── d <== current path
         └── c2
             └── e
    */
    auto old = std::filesystem::current_path();
    auto tmp = std::filesystem::temp_directory_path();
    std::filesystem::current_path(tmp);
    auto d1 = tmp / "a/b/c1/d";
    auto d2 = tmp / "a/b/c2/e";
    std::filesystem::create_directories(d1);
    std::filesystem::create_directories(d2);
    std::filesystem::current_path(d1);

    auto p1 = std::filesystem::path("../../c2/./e");
    auto p2 = std::filesystem::path("../no-such-file");
    std::cout << "Current path is "
              << std::filesystem::current_path() << '\n'
              << "Canonical path for " << p1 << " is "
              << std::filesystem::canonical(p1) << '\n'
              << "Weakly canonical path for " << p2 << " is "
              << std::filesystem::weakly_canonical(p2) << '\n';
    try
    {
        [[maybe_unused]] auto x_x = std::filesystem::canonical(p2);
        // NOT REACHED
    }
    catch (const std::exception& ex)
    {
        std::cout << "Canonical path for " << p2 << " threw exception:\n"
                  << ex.what() << '\n';
    }

    // cleanup
    std::filesystem::current_path(old);
    const auto count = std::filesystem::remove_all(tmp / "a");
    std::cout << "Deleted " << count << " files or directories.\n";
}

🌈 Possible output 🌈

Current path is "/tmp/a/b/c1/d"
Canonical path for "../../c2/./e" is "/tmp/a/b/c2/e"
Weakly canonical path for "../no-such-file" is "/tmp/a/b/c1/no-such-file"
Canonical path for "../no-such-file" threw exception:
filesystem error: in canonical: No such file or directory [../no-such-file] [/tmp/a/b/c1/d]
Deleted 6 files or directories.

πŸ¦‹ Defect reports πŸ¦‹

The following behavior-changing defect reports were applied retroactively to previously published C++ standards. 🫢

  • DR: LWG 2956; Applied to: C++17; Behavior as published: canonical has a spurious base parameter; Correct behavior: removed.

πŸ‘‘ See also πŸ‘‘

  • path β€” represents a path; (C++17); (class)
  • absolute β€” composes an absolute path; (C++17); (function)
  • relative β€” composes a relative path; (C++17); (function)
  • proximate β€” (function); (C++17); no extra blurb in the original, so I'm not inventing one πŸ’…

cute little sign-off bestie: source http://cppreference.com, version/date 2024.06.10, page std::filesy...y_canonical(3) β€” catch you in the filesystem, mwah πŸ’–βœ¨

πŸ“¦ where this page comes from