On Fri, Feb 13, 2026 at 11:17 PM Ivan Lazaric <[email protected]> wrote:
> Changes performed to patch: > Switched to using __glibcxx_format_path in fs_path.h > Inlined __formatter_fs_path and removed > Applied suggestions for faster formatting via string_view > Generalized tests > Updated commit message > > In tests, I am unsure how now test_format_invalid() can be generalized to > Linux, > this is a pretty UTF-16/Windows specific test with a lone surrogate. > For example, path(L"\xd800") throws on Linux. > Also, is the condition path::value_type == wchar_t on it sufficient? > Should I add sizeof(wchar_t) == 2? > I believe that this is indeed sufficient. We could however add a reverse test, path::value_type == char and we do not have valid UTF-8 sentence, I will include that. > > Updated patch follows: > > From 4abf58cdf530411e37a8e05d0d10e45f7c8d6466 Mon Sep 17 00:00:00 2001 > From: Ivan Lazaric <[email protected]> > Date: Mon, 9 Feb 2026 13:26:36 +0100 > Subject: [PATCH] libstdc++: implement formatter for std::filesystem::path > > This patch implements formatting for std::filesystem::path from P2845R8, > and defines the feature test macro __cpp_lib_format_path to 202403L, > provided only in <filesystem>. > > Formatting options are performed (if applicable) in order: > 'g', '?', transcoding, fill-and-align & width > > The standard specifies transcoding behaviour only when literal encoding > is UTF-8, leaving all other cases implementation defined. > Current implementation of filesystem::path assumes: > * char encoding is UTF-8 > * wchar_t encoding is either UTF-32 or UTF-16 > > libstdc++-v3/ChangeLog: > > * include/bits/fs_path.h: Include bits/formatfwd.h. > (formatter<filesystem::path, _CharT>): Define. > * include/bits/version.def: Add format_path. > * include/bits/version.h: Regenerate. > * include/std/filesystem: Expose __cpp_lib_format_path. > * testsuite/std/format/fs_path.cc: New test. > > Signed-off-by: Ivan Lazaric <[email protected]> > Co-authored-by: Jonathan Wakely <[email protected]> > --- > libstdc++-v3/include/bits/fs_path.h | 108 ++++++++++++++++ > libstdc++-v3/include/bits/version.def | 10 ++ > libstdc++-v3/include/bits/version.h | 10 ++ > libstdc++-v3/include/std/filesystem | 1 + > libstdc++-v3/testsuite/std/format/fs_path.cc | 124 +++++++++++++++++++ > 5 files changed, 253 insertions(+) > create mode 100644 libstdc++-v3/testsuite/std/format/fs_path.cc > Thank you, the patch looks good. I will make the minor changes locally and post updated versions here. > > diff --git a/libstdc++-v3/include/bits/fs_path.h > b/libstdc++-v3/include/bits/fs_path.h > Your mail client seems to be breaking lines at 80 columns, and replacing tabs with spaces, which caused patches to not apply cleanly. I have adjusted that manually. > index 07b74de6cbe..cb9e0778a21 100644 > --- a/libstdc++-v3/include/bits/fs_path.h > +++ b/libstdc++-v3/include/bits/fs_path.h > @@ -50,6 +50,10 @@ > # include <compare> > #endif > > +#ifdef __glibcxx_format_path // C++ >= 26 && HOSTED > +# include <bits/formatfwd.h> > +#endif > + > #if defined(_WIN32) && !defined(__CYGWIN__) > # define _GLIBCXX_FILESYSTEM_IS_WINDOWS 1 > #endif > @@ -1451,6 +1455,110 @@ template<> > { return filesystem::hash_value(__p); } > }; > > +#ifdef __glibcxx_format_path // C++ >= 26 && HOSTED > + template<__format::__char _CharT> > + struct formatter<filesystem::path, _CharT> > + { > + formatter() = default; > + > + [[__gnu__::__always_inline__]] > This function is bigger now, and user-facing. I will remove __always_inline__. > + constexpr typename basic_format_parse_context<_CharT>::iterator > + parse(basic_format_parse_context<_CharT>& __pc) > + { > + auto __first = __pc.begin(); > + const auto __last = __pc.end(); > + __format::_Spec<_CharT> __spec{}; > + > + auto __finalize = [this, &__spec] { > + _M_spec = __spec; > + }; > + > + auto __finished = [&] { > + if (__first == __last || *__first == '}') > + { > + __finalize(); > + return true; > + } > + return false; > + }; > + > + if (__finished()) > + return __first; > + > + __first = __spec._M_parse_fill_and_align(__first, __last); > + if (__finished()) > + return __first; > + > + __first = __spec._M_parse_width(__first, __last, __pc); > + if (__finished()) > + return __first; > + > + if (*__first == '?') > + { > + __spec._M_debug = true; > + ++__first; > + } > + if (__finished()) > + return __first; > + > + if (*__first == 'g') > + { > + __spec._M_type = __format::_Pres_g; > + ++__first; > + } > + if (__finished()) > + return __first; > + > + __format::__failed_to_parse_format_spec(); > + } > + > + template<typename _Out> > + typename basic_format_context<_Out, _CharT>::iterator > + format(const filesystem::path& __p, > + basic_format_context<_Out, _CharT>& __fc) const > + { > + using _ValueT = filesystem::path::value_type; > + using _FmtStrT = __format::__formatter_str<_CharT>; > + > + filesystem::path::string_type __s; > + basic_string_view<_ValueT> __sv; > + if (_M_spec._M_type == __format::_Pres_g) > + __sv = __s = __p.generic_string<_ValueT>(); > + else > + __sv = __p.native(); > + > + auto __spec = _M_spec; > + // 'g' should not be passed along. > + __spec._M_type = __format::_Pres_none; > + > + if constexpr (is_same_v<_CharT, _ValueT>) > + return _FmtStrT(__spec).format(__sv, __fc); > + else > + { > + __format::_Str_sink<_ValueT> __sink; > + if (__spec._M_debug) > + { > + using __format::_Term_quote; > + __format::__write_escaped(__sink.out(), __sv, _Term_quote); > + __sv = __sink.view(); > + __spec._M_debug = 0; > + } > + basic_string<_CharT> __out_str; > + using _View = basic_string_view<_ValueT>; > + __out_str.assign_range(__unicode::_Utf_view<_CharT, > _View>(__sv)); > + return _FmtStrT(__spec).format(__out_str, __fc); > + } > + } > + > + constexpr void > + set_debug_format() noexcept > + { _M_spec._M_debug = true; } > + > + private: > + __format::_Spec<_CharT> _M_spec{}; > + }; > +#endif // __glibcxx_format_path > + > _GLIBCXX_END_NAMESPACE_VERSION > } // namespace std > > diff --git a/libstdc++-v3/include/bits/version.def > b/libstdc++-v3/include/bits/version.def > index 4b8e9d43ec2..c7709ba3a07 100644 > --- a/libstdc++-v3/include/bits/version.def > +++ b/libstdc++-v3/include/bits/version.def > @@ -1561,6 +1561,16 @@ ftms = { > }; > }; > > +ftms = { > + name = format_path; > + // 202403 P2845R8 Formatting of std::filesystem::path > + values = { > + v = 202403; > + cxxmin = 26; > + hosted = yes; > + }; > +}; > + > ftms = { > name = freestanding_algorithm; > values = { > diff --git a/libstdc++-v3/include/bits/version.h > b/libstdc++-v3/include/bits/version.h > index 7602225cb6d..c72cda506f1 100644 > --- a/libstdc++-v3/include/bits/version.h > +++ b/libstdc++-v3/include/bits/version.h > @@ -1721,6 +1721,16 @@ > #endif /* !defined(__cpp_lib_format_ranges) */ > #undef __glibcxx_want_format_ranges > > +#if !defined(__cpp_lib_format_path) > +# if (__cplusplus > 202302L) && _GLIBCXX_HOSTED > +# define __glibcxx_format_path 202403L > +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_format_path) > +# define __cpp_lib_format_path 202403L > +# endif > +# endif > +#endif /* !defined(__cpp_lib_format_path) */ > +#undef __glibcxx_want_format_path > + > #if !defined(__cpp_lib_freestanding_algorithm) > # if (__cplusplus >= 202100L) > # define __glibcxx_freestanding_algorithm 202311L > diff --git a/libstdc++-v3/include/std/filesystem > b/libstdc++-v3/include/std/filesystem > index f902c6feb77..b9900f49c33 100644 > --- a/libstdc++-v3/include/std/filesystem > +++ b/libstdc++-v3/include/std/filesystem > @@ -37,6 +37,7 @@ > #include <bits/requires_hosted.h> > > #define __glibcxx_want_filesystem > +#define __glibcxx_want_format_path > #include <bits/version.h> > > #ifdef __cpp_lib_filesystem // C++ >= 17 && HOSTED > diff --git a/libstdc++-v3/testsuite/std/format/fs_path.cc > b/libstdc++-v3/testsuite/std/format/fs_path.cc > new file mode 100644 > index 00000000000..04ff4ac9be8 > --- /dev/null > +++ b/libstdc++-v3/testsuite/std/format/fs_path.cc > @@ -0,0 +1,124 @@ > +// { dg-do run { target c++26 } } > +// { dg-options "-fexec-charset=UTF-8" } > + > +#include <filesystem> > +#include <format> > +#include <testsuite_hooks.h> > + > +using std::filesystem::path; > + > +template<typename... Args> > +bool > +is_format_string_for(const char* str, Args&&... args) > +{ > + try { > + (void) std::vformat(str, std::make_format_args(args...)); > + return true; > + } catch (const std::format_error&) { > + return false; > + } > +} > + > +template<typename... Args> > +bool > +is_format_string_for(const wchar_t* str, Args&&... args) > +{ > + try { > + (void) std::vformat(str, std::make_wformat_args(args...)); > + return true; > + } catch (const std::format_error&) { > + return false; > + } > +} > + > +void > +test_format_spec() > +{ > + // [fs.path.fmtr.funcs] > + // \nontermdef{path-format-spec}\br > + // \opt{fill-and-align} \opt{width} \opt{\terminal{?}} > \opt{\terminal{g}} > + path p; > + VERIFY( is_format_string_for("{}", p) ); > + VERIFY( is_format_string_for("{:}", p) ); > + VERIFY( is_format_string_for("{:?}", p) ); > + VERIFY( is_format_string_for("{:g}", p) ); > + VERIFY( is_format_string_for("{:?g}", p) ); > + VERIFY( is_format_string_for("{:?g}", p) ); > + VERIFY( is_format_string_for("{:F^32?g}", p) ); > + VERIFY( is_format_string_for("{:G<{}?g}", p, 32) ); > + VERIFY( is_format_string_for(L"{:G<{}?g}", p, 32) ); > + > + VERIFY( ! is_format_string_for("{:g?}", p) ); > +} > + > +#define WIDEN_(C, S) ::std::__format::_Widen<C>(S, L##S) > +#define WFMT(S) WIDEN_(_FCharT, S) > +#define WPATH(S) WIDEN_(_PCharT, S) > + > +template<typename _FCharT, typename _PCharT> > +void > +test_format() > +{ > + std::basic_string<_FCharT> res; > + res = std::format(WFMT("{}"), path(WPATH("/usr/include"))); > + VERIFY( res == WFMT("/usr/include") ); > + res = std::format(WFMT("{:.<10}"), path(WPATH("foo/bar"))); > + VERIFY( res == WFMT("foo/bar...") ); > + res = std::format(WFMT("{}"), path(WPATH("foo///bar"))); > + VERIFY( res == WFMT("foo///bar") ); > + res = std::format(WFMT("{:g}"), path(WPATH("foo///bar"))); > + VERIFY( res == WFMT("foo/bar") ); > + res = std::format(WFMT("{}"), path(WPATH("/path/with/new\nline"))); > + VERIFY( res == WFMT("/path/with/new\nline") ); > + res = std::format(WFMT("{:?}"), path(WPATH("multi\nline"))); > + VERIFY( res == WFMT("\"multi\\nline\"") ); > + res = std::format(WFMT("{:?g}"), path(WPATH("mu///lti\nli///ne"))); > + VERIFY( res == WFMT("\"mu/lti\\nli/ne\"") ); > + res = std::format(WFMT("{}"), > > path(WPATH("\u0428\u0447\u0443\u0447\u044B\u043D\u0448\u0447\u044B\u043D\u0430"))); > + VERIFY( res == > WFMT("\u0428\u0447\u0443\u0447\u044B\u043D\u0448\u0447\u044B\u043D\u0430") > ); > + > + if constexpr (path::preferred_separator == L'\\') > + { > + res = std::format(WFMT("{}"), path(WPATH("C:\\foo\\bar"))); > + VERIFY( res == WFMT("C:\\foo\\bar") ); > + res = std::format(WFMT("{:g}"), path(WPATH("C:\\foo\\bar"))); > + VERIFY( res == WFMT("C:/foo/bar") ); > + } > +} > + > +void > +test_format_invalid() > +{ > + if constexpr (std::is_same_v<path::value_type, wchar_t>) > + { > + std::string res; > + > + path p(L"\xd800"); > + res = std::format("{}", p); > + VERIFY( res == "\uFFFD" ); > + res = std::format("{:?}", p); > + VERIFY( res == "\"\\x{d800}\"" ); > + > + path p2(L"///\xd800"); > + res = std::format("{}", p2); > + VERIFY( res == "///\uFFFD" ); > + res = std::format("{:g}", p2); > + VERIFY( res == "/\uFFFD" ); > + res = std::format("{:?}", p2); > + VERIFY( res == "\"///\\x{d800}\"" ); > + res = std::format("{:?g}", p2); > + VERIFY( res == "\"/\\x{d800}\"" ); > + res = std::format("{:C>14?g}", p2); > + VERIFY( res == "CCC\"/\\x{d800}\"" ); > + } > +} > + > +int main() > +{ > + test_format_spec(); > + test_format<char, char>(); > + test_format<char, wchar_t>(); > + test_format<wchar_t, char>(); > + test_format<wchar_t, wchar_t>(); > + test_format_invalid(); > +} > -- > 2.43.0 > > On Thu, Feb 12, 2026 at 1:31 PM Jonathan Wakely <[email protected]> > wrote: > > > > On Wed, 11 Feb 2026 at 13:56, Ivan Lazaric <[email protected]> > wrote: > > > > > > This patch implements formatting for std::filesystem::path from > P2845R8, > > > and defines the feature test macro __cpp_lib_format_path to 202403L, > > > provided only in <filesystem>. > > > > > > libstdc++-v3/ChangeLog: > > > * include/bits/fs_path.h: Include bits/formatfwd.h. > > > (__format::__formatter_fs_path<_CharT>) > > > (formatter<filesystem::path, _CharT>): Define. > > > * include/bits/version.def: Add format_path. > > > * include/bits/version.h: Regenerate. > > > * include/std/filesystem: Expose __cpp_lib_format_path. > > > * testsuite/std/format/fs_path.cc: New test. > > > > > > Signed-off-by: Ivan Lazaric <[email protected]> > > > Co-authored-by: Jonathan Wakely <[email protected]> > > > --- > > > libstdc++-v3/include/bits/fs_path.h | 136 +++++++++++++++++++ > > > libstdc++-v3/include/bits/version.def | 10 ++ > > > libstdc++-v3/include/bits/version.h | 10 ++ > > > libstdc++-v3/include/std/filesystem | 1 + > > > libstdc++-v3/testsuite/std/format/fs_path.cc | 88 ++++++++++++ > > > 5 files changed, 245 insertions(+) > > > create mode 100644 libstdc++-v3/testsuite/std/format/fs_path.cc > > > > > > diff --git a/libstdc++-v3/include/bits/fs_path.h > > > b/libstdc++-v3/include/bits/fs_path.h > > > index 07b74de6cbe..1ca4942235e 100644 > > > --- a/libstdc++-v3/include/bits/fs_path.h > > > +++ b/libstdc++-v3/include/bits/fs_path.h > > > @@ -50,6 +50,10 @@ > > > # include <compare> > > > #endif > > > > > > +#ifdef __cpp_lib_format_path // C++ >= 26 && HOSTED > > > > I should have noticed this earlier, but this should really be testing > > __glibcxx_format_path instead. > > > > In this case it doesn't matter, because fs_path.h is only included by > > <filesystem> which defines __cpp_lib_format_path. But if we included > > fs_path.h elsewhere in the library without the rest of <filesystem> > > then __cpp_lib_format_path would not be defined, whereas > > __glibcxx_format_path would be. > > > > tl;dr most internal uses of feature test macros should use > > __glibcxx_xxx rather than __cpp_lib_xxx. > > > > > > > > > > > +# include <bits/formatfwd.h> > > > +#endif > > > + > > > #if defined(_WIN32) && !defined(__CYGWIN__) > > > # define _GLIBCXX_FILESYSTEM_IS_WINDOWS 1 > > > #endif > > > @@ -1451,6 +1455,138 @@ template<> > > > { return filesystem::hash_value(__p); } > > > }; > > > > > > +#ifdef __cpp_lib_format_path // C++ >= 26 && HOSTED > > > +namespace __format > > > +{ > > > + template<__char _CharT> > > > + struct __formatter_fs_path > > > + { > > > + __formatter_fs_path() = default; > > > + > > > + constexpr > > > + __formatter_fs_path(_Spec<_CharT> __spec) noexcept > > > + : _M_spec(__spec) > > > + { } > > > + > > > + constexpr typename basic_format_parse_context<_CharT>::iterator > > > + parse(basic_format_parse_context<_CharT>& __pc) > > > + { > > > + auto __first = __pc.begin(); > > > + const auto __last = __pc.end(); > > > + _Spec<_CharT> __spec{}; > > > + > > > + auto __finalize = [this, &__spec] { > > > + _M_spec = __spec; > > > + }; > > > + > > > + auto __finished = [&] { > > > + if (__first == __last || *__first == '}') > > > + { > > > + __finalize(); > > > + return true; > > > + } > > > + return false; > > > + }; > > > + > > > + if (__finished()) > > > + return __first; > > > + > > > + __first = __spec._M_parse_fill_and_align(__first, __last); > > > + if (__finished()) > > > + return __first; > > > + > > > + __first = __spec._M_parse_width(__first, __last, __pc); > > > + if (__finished()) > > > + return __first; > > > + > > > + if (*__first == '?') > > > + { > > > + __spec._M_debug = true; > > > + ++__first; > > > + } > > > + if (__finished()) > > > + return __first; > > > + > > > + if (*__first == 'g') > > > + { > > > + __spec._M_type = _Pres_g; > > > + ++__first; > > > + } > > > + if (__finished()) > > > + return __first; > > > + > > > + __format::__failed_to_parse_format_spec(); > > > + } > > > + > > > + template<typename _Out> > > > + _Out > > > + format(const filesystem::path& __p, > > > + basic_format_context<_Out, _CharT>& __fc) const > > > + { > > > + using _ValueT = filesystem::path::value_type; > > > + using _FmtStrT = __formatter_str<_CharT>; > > > + > > > + filesystem::path::string_type __s; > > > + if (_M_spec._M_type == _Pres_g) > > > + __s = __p.generic_string<_ValueT>(); > > > + else > > > + __s = __p.native(); > > > + > > > + auto __spec = _M_spec; > > > + // 'g' should not be passed along. > > > + __spec._M_type = _Pres_none; > > > + > > > + if constexpr (is_same_v<_CharT, _ValueT>) > > > + return _FmtStrT(__spec).format(__s, __fc); > > > + else > > > + { > > > + if (__spec._M_debug) > > > + { > > > + _Str_sink<_ValueT> __sink; > > > + basic_string_view<_ValueT> __sv(__s); > > > + __format::__write_escaped(__sink.out(), __sv, _Term_quote); > > > + __s = std::move(__sink).get(); > > > + __spec._M_debug = 0; > > > + } > > > + basic_string<_CharT> __out_str; > > > + using _View = basic_string_view<_ValueT>; > > > + __out_str.assign_range(__unicode::_Utf_view<_CharT, > _View>(__s)); > > > + return _FmtStrT(__spec).format(__out_str, __fc); > > > + } > > > + } > > > + > > > + constexpr void > > > + set_debug_format() noexcept > > > + { _M_spec._M_debug = true; } > > > + > > > + private: > > > + _Spec<_CharT> _M_spec{}; > > > + }; > > > +} // namespace __format > > > + > > > + template<__format::__char _CharT> > > > + struct formatter<filesystem::path, _CharT> > > > + { > > > + formatter() = default; > > > + > > > + [[__gnu__::__always_inline__]] > > > + constexpr typename basic_format_parse_context<_CharT>::iterator > > > + parse(basic_format_parse_context<_CharT>& __pc) > > > + { return _M_f.parse(__pc); } > > > + > > > + template<typename _Out> > > > + typename basic_format_context<_Out, _CharT>::iterator > > > + format(const filesystem::path& __p, > > > + basic_format_context<_Out, _CharT>& __fc) const > > > + { return _M_f.format(__p, __fc); } > > > + > > > + constexpr void set_debug_format() noexcept { > _M_f.set_debug_format(); } > > > + > > > + private: > > > + __format::__formatter_fs_path<_CharT> _M_f; > > > + }; > > > +#endif // __cpp_lib_format_path > > > + > > > _GLIBCXX_END_NAMESPACE_VERSION > > > } // namespace std > > > > > > diff --git a/libstdc++-v3/include/bits/version.def > > > b/libstdc++-v3/include/bits/version.def > > > index 4b8e9d43ec2..c7709ba3a07 100644 > > > --- a/libstdc++-v3/include/bits/version.def > > > +++ b/libstdc++-v3/include/bits/version.def > > > @@ -1561,6 +1561,16 @@ ftms = { > > > }; > > > }; > > > > > > +ftms = { > > > + name = format_path; > > > + // 202403 P2845R8 Formatting of std::filesystem::path > > > + values = { > > > + v = 202403; > > > + cxxmin = 26; > > > + hosted = yes; > > > + }; > > > +}; > > > + > > > ftms = { > > > name = freestanding_algorithm; > > > values = { > > > diff --git a/libstdc++-v3/include/bits/version.h > > > b/libstdc++-v3/include/bits/version.h > > > index 7602225cb6d..c72cda506f1 100644 > > > --- a/libstdc++-v3/include/bits/version.h > > > +++ b/libstdc++-v3/include/bits/version.h > > > @@ -1721,6 +1721,16 @@ > > > #endif /* !defined(__cpp_lib_format_ranges) */ > > > #undef __glibcxx_want_format_ranges > > > > > > +#if !defined(__cpp_lib_format_path) > > > +# if (__cplusplus > 202302L) && _GLIBCXX_HOSTED > > > +# define __glibcxx_format_path 202403L > > > +# if defined(__glibcxx_want_all) || > defined(__glibcxx_want_format_path) > > > +# define __cpp_lib_format_path 202403L > > > +# endif > > > +# endif > > > +#endif /* !defined(__cpp_lib_format_path) */ > > > +#undef __glibcxx_want_format_path > > > + > > > #if !defined(__cpp_lib_freestanding_algorithm) > > > # if (__cplusplus >= 202100L) > > > # define __glibcxx_freestanding_algorithm 202311L > > > diff --git a/libstdc++-v3/include/std/filesystem > > > b/libstdc++-v3/include/std/filesystem > > > index f902c6feb77..b9900f49c33 100644 > > > --- a/libstdc++-v3/include/std/filesystem > > > +++ b/libstdc++-v3/include/std/filesystem > > > @@ -37,6 +37,7 @@ > > > #include <bits/requires_hosted.h> > > > > > > #define __glibcxx_want_filesystem > > > +#define __glibcxx_want_format_path > > > #include <bits/version.h> > > > > > > #ifdef __cpp_lib_filesystem // C++ >= 17 && HOSTED > > > diff --git a/libstdc++-v3/testsuite/std/format/fs_path.cc > > > b/libstdc++-v3/testsuite/std/format/fs_path.cc > > > new file mode 100644 > > > index 00000000000..5105e73f611 > > > --- /dev/null > > > +++ b/libstdc++-v3/testsuite/std/format/fs_path.cc > > > @@ -0,0 +1,88 @@ > > > +// { dg-do run { target c++26 } } > > > + > > > +#include <filesystem> > > > +#include <format> > > > +#include <testsuite_hooks.h> > > > + > > > +using std::filesystem::path; > > > + > > > +template<typename... Args> > > > +bool > > > +is_format_string_for(const char* str, Args&&... args) > > > +{ > > > + try { > > > + (void) std::vformat(str, std::make_format_args(args...)); > > > + return true; > > > + } catch (const std::format_error&) { > > > + return false; > > > + } > > > +} > > > + > > > +template<typename... Args> > > > +bool > > > +is_format_string_for(const wchar_t* str, Args&&... args) > > > +{ > > > + try { > > > + (void) std::vformat(str, std::make_wformat_args(args...)); > > > + return true; > > > + } catch (const std::format_error&) { > > > + return false; > > > + } > > > +} > > > + > > > +void > > > +test_format_spec() > > > +{ > > > + // [fs.path.fmtr.funcs] > > > + // \nontermdef{path-format-spec}\br > > > + // \opt{fill-and-align} \opt{width} \opt{\terminal{?}} > \opt{\terminal{g}} > > > + path p; > > > + VERIFY( is_format_string_for("{}", p) ); > > > + VERIFY( is_format_string_for("{:}", p) ); > > > + VERIFY( is_format_string_for("{:?}", p) ); > > > + VERIFY( is_format_string_for("{:g}", p) ); > > > + VERIFY( is_format_string_for("{:?g}", p) ); > > > + VERIFY( is_format_string_for("{:?g}", p) ); > > > + VERIFY( is_format_string_for("{:F^32?g}", p) ); > > > + VERIFY( is_format_string_for("{:G<{}?g}", p, 32) ); > > > + VERIFY( is_format_string_for(L"{:G<{}?g}", p, 32) ); > > > + > > > + VERIFY( ! is_format_string_for("{:g?}", p) ); > > > +} > > > + > > > +void > > > +test_format_path() > > > +{ > > > + VERIFY( std::format("{}", path("/usr/include")) == "/usr/include" ); > > > + VERIFY( std::format("{:.<10}", path("foo/bar")) == "foo/bar..." ); > > > + VERIFY( std::format("{}", path("foo///bar")) == "foo///bar" ); > > > + VERIFY( std::format("{:g}", path("foo///bar")) == "foo/bar" ); > > > + VERIFY( std::format("{}", path("/path/with/new\nline")) == > > > "/path/with/new\nline" ); > > > + VERIFY( std::format("{:?}", path("multi\nline")) == > "\"multi\\nline\"" ); > > > + VERIFY( std::format("{:?g}", path("mu///lti\nli///ne")) == > > > "\"mu/lti\\nli/ne\"" ); > > > + VERIFY( std::format(L"{}", > > > > path(L"\u0428\u0447\u0443\u0447\u044B\u043D\u0448\u0447\u044B\u043D\u0430")) > > > == > L"\u0428\u0447\u0443\u0447\u044B\u043D\u0448\u0447\u044B\u043D\u0430" > > > ); > > > + > > > + if constexpr (path::preferred_separator == L'\\') > > > + { > > > + VERIFY( std::format("{}", path("C:\\foo\\bar")) == "C:\\foo\\bar" > ); > > > + VERIFY( std::format("{:g}", path("C:\\foo\\bar")) == "C:/foo/bar" > ); > > > + > > > + path p(L"\xd800"); > > > + VERIFY( std::format("{}", p) == "\uFFFD" ); > > > + VERIFY( std::format("{:?}", p) == "\"\\x{d800}\"" ); > > > + VERIFY( std::format(L"{:?}", p) == L"\"\\x{d800}\"" ); > > > + > > > + path p2(L"///\xd800"); > > > + VERIFY( std::format("{}", p2) == "///\uFFFD" ); > > > + VERIFY( std::format("{:g}", p2) == "/\uFFFD" ); > > > + VERIFY( std::format("{:?}", p2) == "\"///\\x{d800}\"" ); > > > + VERIFY( std::format("{:?g}", p2) == "\"/\\x{d800}\"" ); > > > + VERIFY( std::format("{:C>14?g}", p2) == "CCC\"/\\x{d800}\"" ); > > > + } > > > +} > > > + > > > +int main() > > > +{ > > > + test_format_spec(); > > > + test_format_path(); > > > +} > > > -- > > > 2.43.0 > > > > > > >
