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
> > >
> >
>
>

Reply via email to