On Windows fmt::format(L"{:g}", std::filesystem::path(L"C:\\foo")) returned L"C:\\foo", while fmt::format("{:g}", ...) and fmt::format(L"{:?g}", ...) give C:/foo. get_path_string used the native or generic string computed by the formatter only when converting wchar_t to UTF-8 and otherwise called p.string<Char>(), so g was ignored whenever Char is path::value_type.
This uses the passed string in that case, as write_escaped_path already does. On POSIX it also makes narrow {:g} use generic_string(), which can differ from the native string only where the standard library normalizes separators (libstdc++ collapses repeated ones), matching {:?g}.
- Ran:
std-testwith MSVC 2022 in C++17 and C++20 (27 and 29 tests pass, the new assertion fails without the fix) and C++14 (23 pass), plusxchar-test(38 pass) andcompile-test(22 pass) in C++17. - Checked: clang-format 21 on
include/fmt/std.hreports no changes.
Activity
5@skyon0522-ai Confirmed with GCC 13.3 on Linux: for path("a//b"), L"{:g}" gave a//b and L"{:?g}" gave "a//b", while generic_wstring() is a/b.
Fixed in 0fe44d5. The cross-character branches of get_path_string and write_escaped_path now convert the string the formatter already selected (path(native).string<Char>()) instead of calling p.string<Char>(). std_test.path has new a//b checks against generic_string() and generic_wstring(). The two wide ones fail without the change, and std-test passes with it (GCC 13.3, C++17).
@skyon0522-ai please don't post AI slop or you will be banned.
[AI-assisted review] POSIX wide
gstill uses the native pathThere seems to be one remaining cross-character case in this fix. On Linux, a path with repeated separators has different native and generic representations:
std::filesystem::path("a//b").generic_string()isa/b.This standalone example uses the public formatter API (C++17, header-only):
At PR head
adb4f149c0655c3d12e033cff11f5830b5234bf9, compiled with GCC 13.3.0 on Linux:At base
0fc31633e7d53ac895ae0a688b83b7d44f0b0c1f, onlynarrow gdiffers (a//b); the wide results are the same. Thus the PR fixes the narrow case while the wide case remains.The formatter selects the generic path string, but for POSIX
Char=wchar_t/path::value_type=char,get_path_stringfalls through top.string<Char>(), reading the native path again. The escaped-path helper has the analogous cross-character branch.I infer from the shared
gparser and generic/native selection that wide output should preserve the generic representation too. If that is intended, could the cross-character conversion retain that selection, with a POSIXwchar_ttest for this case?Prepared with Codex assistance; a separate Codex reviewer rebuilt the pinned base/head comparison. This is focused Linux/GCC reproduction with ordinary ASCII input. The full fmt suite and Windows/other-OS behavior were not run locally.