On 14/05/25 10:01 +0200, Tomasz Kamiński wrote:
This commits adjust the way how the arguments are stored in the _Arg_value
(and thus basic_format_args), by preserving the types of fixed width
floating-point types, that were previously converted to float, double,
long double.

The _Arg_value union now contains alternatives with std::bfloat16_t,
std::float16_t, std::float32_t, std::float64_t that use pre-existing
_Arg_bf16, _Arg_f16, _Arg_f32, _Arg_f32 argument types.

This does not affect formatting, as specialization of formatters for fixed
width floating-point types formats them by casting to the corresponding
standard floating point type.

For the 128bit floating we need to handle the ppc64 architecture,
(_GLIBCXX_LONG_DOUBLE_ALT128_COMPAT) for which the long double may (per TU
basis) designate either __ibm128 and __ieee128 type, we need to store both
types in the _Arg_value and have two _Arg_types (_Arg_ibm128, _Arg_ieee128).
On other architectures we use extra enumerator value to store __float128,
that is different from long double and _Float128. This is consistent with ppc64,
for which __float128, if present, is same type as __ieee128. We use 
_Arg_float128
_M_float128 names that deviate from _Arg_fN naming scheme, to emphasize that
this flag is not used for std::float128_t (_Float128) type, that is consistenly
formatted via handle.

The __format::__float128_t type is renamed to __format::__flt128_t, to mitigate
visual confusion between this type and __float128. We also introduce __bflt16_t
typedef instead of using of decltype.

We add new alternative for the _Arg_value and allow them to be accessed via 
_S_get,
when the types are available. However, we produce and handle corresponding 
_Arg_type,
only when we can format them. See also r14-3329-g27d0cfcb2b33de.

The formatter<_Float128, _CharT> that formats via __format::__flt128_t is always
provided, when type is available. It is still correct when __format::__flt128_t
is _Float128.

We also provide formatter<__float128, _CharT> that formats via __flt128_t.
As this type may be disabled (-mno-float128), extra care needs to be taken,
for situation when __float128 is same as long double. If the formatter would be
defined in such case, the formatter<long double, _CharT> would be generated
from different specializations, and have different mangling:
 * formatter<__float128, _CharT> if __float128 is present,
 * formatter<__format::__formattable_float, _CharT> otherwise.
To best of my knowledge this happens only on ppc64 for __ieee128 and __float128,
so the formatter is not defined in this case. static_assert is added to detect
other configurations like that. In such case we should replace it with 
constraint.

        PR libstdc++/119246

libstdc++-v3/ChangeLog:

        * include/std/format (__format::__bflt16_t): Define.
        (_GLIBCXX_FORMAT_F128): Separate value for cases where _Float128
        is used.
        (__format::__float128_t): Renamed to __format::__flt128_t.
        (std::formatter<_Float128, _CharT>): Define always if there is
        formattable 128bit float.
        (std::formatter<__float128, _CharT>): Define.
        (_Arg_type::_Arg_f128): Rename to _Arg_float128 and adjust value.
        (_Arg_type::_Arg_ibm128): Change value to _Arg_ldbl.
        (_Arg_type::_Arg_ieee128): Define as alias to _Arg_float128.
        (_Arg_value::_M_f128): Replaced with _M_ieee128 and _M_float128.
        (_Arg_value::_M_ieee128, _Arg_value::_M_float128)
        (_Arg_value::_M_bf16, _Arg_value::_M_f16, _Arg_value::_M_f32)
         _Arg_value::_M_f64): Define.
        (_Arg_value::_S_get, basic_format_arg::_S_to_enum): Handle __bflt16,
        _Float16, _Float32, _Float64, and __float128 types.
        (basic_format_arg::_S_to_arg_type): Preserve _bflt16, _Float16,
        _Float32, _Float64 and __float128 types.
        (basic_format_arg::_M_visit): Handle _Arg_float128, _Arg_ieee128,
        _Arg_b16, _Arg_f16, _Arg_f32, _Arg_f64.
        * testsuite/std/format/arguments/args.cc: Updated to illustrate
        that extended floating point types use handles now. Added test
        for __float128.
        * testsuite/std/format/parse_ctx.cc: Extended test to cover class
        to check_dynamic_spec with floating point types and handles.
---
I believe I have fixed all the typos. OK for trunk?


OK, thanks

libstdc++-v3/include/std/format               | 217 ++++++++++++------
.../testsuite/std/format/arguments/args.cc    |  45 ++--
.../testsuite/std/format/parse_ctx.cc         |  72 +++++-
3 files changed, 227 insertions(+), 107 deletions(-)

diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format
index b3192cf2868..f0b0252255d 100644
--- a/libstdc++-v3/include/std/format
+++ b/libstdc++-v3/include/std/format
@@ -1863,20 +1863,24 @@ namespace __format
      _Spec<_CharT> _M_spec{};
    };

+#ifdef __BFLT16_DIG__
+   using __bflt16_t = decltype(0.0bf16);
+#endif
+
  // Decide how 128-bit floating-point types should be formatted (or not).
-  // When supported, the typedef __format::__float128_t is the type that
-  // format arguments should be converted to for storage in basic_format_arg.
+  // When supported, the typedef __format::__flt128_t is the type that format
+  // arguments should be converted to before passing them to __formatter_fp.
  // Define the macro _GLIBCXX_FORMAT_F128 to say they're supported.
-  // _GLIBCXX_FORMAT_F128=1 means __float128, _Float128 etc. will be formatted
-  // by converting them to long double (or __ieee128 for powerpc64le).
-  // _GLIBCXX_FORMAT_F128=2 means basic_format_arg needs to enable explicit
-  // support for _Float128, rather than formatting it as another type.
+  // The __float128, _Float128 will be formatted by converting them to:
+  // __ieee128 (same as __float128) when _GLIBCXX_FORMAT_F128=1,
+  // long double when _GLIBCXX_FORMAT_F128=2,
+  // _Float128 when _GLIBCXX_FORMAT_F128=3.
#undef _GLIBCXX_FORMAT_F128

#ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT

  // Format 128-bit floating-point types using __ieee128.
-  using __float128_t = __ieee128;
+  using __flt128_t = __ieee128;
# define _GLIBCXX_FORMAT_F128 1

#ifdef __LONG_DOUBLE_IEEE128__
@@ -1910,14 +1914,14 @@ namespace __format
#elif defined _GLIBCXX_LDOUBLE_IS_IEEE_BINARY128

  // Format 128-bit floating-point types using long double.
-  using __float128_t = long double;
-# define _GLIBCXX_FORMAT_F128 1
+  using __flt128_t = long double;
+# define _GLIBCXX_FORMAT_F128 2

#elif __FLT128_DIG__ && defined(_GLIBCXX_HAVE_FLOAT128_MATH)

  // Format 128-bit floating-point types using _Float128.
-  using __float128_t = _Float128;
-# define _GLIBCXX_FORMAT_F128 2
+  using __flt128_t = _Float128;
+# define _GLIBCXX_FORMAT_F128 3

# if __cplusplus == 202002L
  // These overloads exist in the library, but are not declared for C++20.
@@ -2947,8 +2951,8 @@ namespace __format
    };
#endif

-#if defined(__FLT128_DIG__) && _GLIBCXX_FORMAT_F128 == 1
-  // Reuse __formatter_fp<C>::format<__float128_t, Out> for _Float128.
+#if defined(__FLT128_DIG__) && _GLIBCXX_FORMAT_F128
+  // Use __formatter_fp<C>::format<__format::__flt128_t, Out> for _Float128.
  template<__format::__char _CharT>
    struct formatter<_Float128, _CharT>
    {
@@ -2962,17 +2966,45 @@ namespace __format
      template<typename _Out>
        typename basic_format_context<_Out, _CharT>::iterator
        format(_Float128 __u, basic_format_context<_Out, _CharT>& __fc) const
-       { return _M_f.format((__format::__float128_t)__u, __fc); }
+       { return _M_f.format((__format::__flt128_t)__u, __fc); }
+
+    private:
+      __format::__formatter_fp<_CharT> _M_f;
+    };
+#endif
+
+#if defined(__SIZEOF_FLOAT128__) && _GLIBCXX_FORMAT_F128 != 1
+  // Reuse __formatter_fp<C>::format<__format::__flt128_t, Out> for __float128.
+  // This formatter is not declared if _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT is 
true,
+  // as __float128 when present is same type as __ieee128, which may be same as
+  // long double.
+  template<__format::__char _CharT>
+    struct formatter<__float128, _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(__float128 __u, basic_format_context<_Out, _CharT>& __fc) const
+       { return _M_f.format((__format::__flt128_t)__u, __fc); }

    private:
      __format::__formatter_fp<_CharT> _M_f;
+
+      static_assert( !is_same_v<__float128, long double>,
+                    "This specialization should not be used for long double" );
    };
#endif

#if defined(__STDCPP_BFLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
  // Reuse __formatter_fp<C>::format<float, Out> for bfloat16_t.
  template<__format::__char _CharT>
-    struct formatter<__gnu_cxx::__bfloat16_t, _CharT>
+    struct formatter<__format::__bflt16_t, _CharT>
    {
      formatter() = default;

@@ -3835,16 +3867,14 @@ namespace __format
  enum _Arg_t : unsigned char {
    _Arg_none, _Arg_bool, _Arg_c, _Arg_i, _Arg_u, _Arg_ll, _Arg_ull,
    _Arg_flt, _Arg_dbl, _Arg_ldbl, _Arg_str, _Arg_sv, _Arg_ptr, _Arg_handle,
-    _Arg_i128, _Arg_u128,
-    _Arg_bf16, _Arg_f16, _Arg_f32, _Arg_f64, // These are unused.
+    _Arg_i128, _Arg_u128, _Arg_float128,
+    _Arg_bf16, _Arg_f16, _Arg_f32, _Arg_f64,
+    _Arg_max_,
+
#ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT
-    _Arg_next_value_,
-    _Arg_f128 = _Arg_ldbl,
-    _Arg_ibm128 = _Arg_next_value_,
-#else
-    _Arg_f128,
+    _Arg_ibm128 = _Arg_ldbl,
+    _Arg_ieee128 = _Arg_float128,
#endif
-    _Arg_max_
  };

  template<typename _Context>
@@ -3871,6 +3901,12 @@ namespace __format
        double _M_dbl;
#ifndef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT // No long double if it's ambiguous.
        long double _M_ldbl;
+#else
+       __ibm128  _M_ibm128;
+       __ieee128 _M_ieee128;
+#endif
+#ifdef __SIZEOF_FLOAT128__
+       __float128 _M_float128;
#endif
        const _CharT* _M_str;
        basic_string_view<_CharT> _M_sv;
@@ -3880,11 +3916,17 @@ namespace __format
        __int128 _M_i128;
        unsigned __int128 _M_u128;
#endif
-#ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT
-       __ieee128 _M_f128;
-       __ibm128  _M_ibm128;
-#elif _GLIBCXX_FORMAT_F128 == 2
-       __float128_t _M_f128;
+#ifdef __BFLT16_DIG__
+       __bflt16_t _M_bf16;
+#endif
+#ifdef __FLT16_DIG__
+       _Float16 _M_f16;
+#endif
+#ifdef __FLT32_DIG__
+       _Float32 _M_f32;
+#endif
+#ifdef __FLT64_DIG__
+       _Float64 _M_f64;
#endif
      };

@@ -3922,10 +3964,14 @@ namespace __format
          else if constexpr (is_same_v<_Tp, long double>)
            return __u._M_ldbl;
#else
-         else if constexpr (is_same_v<_Tp, __ieee128>)
-           return __u._M_f128;
          else if constexpr (is_same_v<_Tp, __ibm128>)
            return __u._M_ibm128;
+         else if constexpr (is_same_v<_Tp, __ieee128>)
+           return __u._M_ieee128;
+#endif
+#ifdef __SIZEOF_FLOAT128__
+         else if constexpr (is_same_v<_Tp, __float128>)
+           return __u._M_float128;
#endif
          else if constexpr (is_same_v<_Tp, const _CharT*>)
            return __u._M_str;
@@ -3939,9 +3985,21 @@ namespace __format
          else if constexpr (is_same_v<_Tp, unsigned __int128>)
            return __u._M_u128;
#endif
-#if _GLIBCXX_FORMAT_F128 == 2
-         else if constexpr (is_same_v<_Tp, __float128_t>)
-           return __u._M_f128;
+#ifdef __BFLT16_DIG__
+         else if constexpr (is_same_v<_Tp, __bflt16_t>)
+           return __u._M_bf16;
+#endif
+#ifdef __FLT16_DIG__
+         else if constexpr (is_same_v<_Tp, _Float16>)
+           return __u._M_f16;
+#endif
+#ifdef __FLT32_DIG__
+         else if constexpr (is_same_v<_Tp, _Float32>)
+           return __u._M_f32;
+#endif
+#ifdef __FLT64_DIG__
+         else if constexpr (is_same_v<_Tp, _Float64>)
+           return __u._M_f64;
#endif
          else if constexpr (derived_from<_Tp, _HandleBase>)
            return static_cast<_Tp&>(__u._M_handle);
@@ -4120,36 +4178,25 @@ namespace __format
          else if constexpr (is_same_v<_Td, __ieee128>)
            return type_identity<__ieee128>();
#endif
-
-#if defined(__FLT16_DIG__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
-         else if constexpr (is_same_v<_Td, _Float16>)
-           return type_identity<float>();
+#if defined(__SIZEOF_FLOAT128__) && _GLIBCXX_FORMAT_F128
+         else if constexpr (is_same_v<_Td, __float128>)
+           return type_identity<__float128>();
#endif
-
-#if defined(__BFLT16_DIG__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
-         else if constexpr (is_same_v<_Td, decltype(0.0bf16)>)
-           return type_identity<float>();
+#if defined(__STDCPP_BFLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
+         else if constexpr (is_same_v<_Td, __format::__bflt16_t>)
+           return type_identity<__format::__bflt16_t>();
+#endif
+#if defined(__STDCPP_FLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
+         else if constexpr (is_same_v<_Td, _Float16>)
+           return type_identity<_Float16>();
#endif
-
#if defined(__FLT32_DIG__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
          else if constexpr (is_same_v<_Td, _Float32>)
-           return type_identity<float>();
+           return type_identity<_Float32>();
#endif
-
#if defined(__FLT64_DIG__) && defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY64)
          else if constexpr (is_same_v<_Td, _Float64>)
-           return type_identity<double>();
-#endif
-
-#if _GLIBCXX_FORMAT_F128
-# if __FLT128_DIG__
-         else if constexpr (is_same_v<_Td, _Float128>)
-           return type_identity<__format::__float128_t>();
-# endif
-# if __SIZEOF_FLOAT128__
-         else if constexpr (is_same_v<_Td, __float128>)
-           return type_identity<__format::__float128_t>();
-# endif
+           return type_identity<_Float64>();
#endif
          else if constexpr (__is_specialization_of<_Td, basic_string_view>
                            || __is_specialization_of<_Td, basic_string>)
@@ -4205,7 +4252,27 @@ namespace __format
          else if constexpr (is_same_v<_Tp, __ibm128>)
            return _Arg_ibm128;
          else if constexpr (is_same_v<_Tp, __ieee128>)
-           return _Arg_f128;
+           return _Arg_ieee128;
+#endif
+#if defined(__SIZEOF_FLOAT128__) && _GLIBCXX_FORMAT_F128
+         else if constexpr (is_same_v<_Tp, __float128>)
+           return _Arg_float128;
+#endif
+#if defined(__STDCPP_BFLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
+         else if constexpr (is_same_v<_Tp, __format::__bflt16_t>)
+           return _Arg_bf16;
+#endif
+#if defined(__STDCPP_FLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
+         else if constexpr (is_same_v<_Tp, _Float16>)
+           return _Arg_f16;
+#endif
+#if defined(__FLT32_DIG__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
+         else if constexpr (is_same_v<_Tp, _Float32>)
+           return _Arg_f32;
+#endif
+#if defined(__FLT64_DIG__) && defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY64)
+         else if constexpr (is_same_v<_Tp, _Float64>)
+           return _Arg_f64;
#endif
          else if constexpr (is_same_v<_Tp, const _CharT*>)
            return _Arg_str;
@@ -4219,11 +4286,6 @@ namespace __format
          else if constexpr (is_same_v<_Tp, unsigned __int128>)
            return _Arg_u128;
#endif
-
-#if _GLIBCXX_FORMAT_F128 == 2
-         else if constexpr (is_same_v<_Tp, __format::__float128_t>)
-           return _Arg_f128;
-#endif
          else if constexpr (is_same_v<_Tp, handle>)
            return _Arg_handle;
        }
@@ -4296,13 +4358,33 @@ namespace __format
#ifndef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT
            case _Arg_ldbl:
              return std::forward<_Visitor>(__vis)(_M_val._M_ldbl);
+#if defined(__SIZEOF_FLOAT128__) && _GLIBCXX_FORMAT_F128
+           case _Arg_float128:
+             return std::forward<_Visitor>(__vis)(_M_val._M_float128);
+#endif
#else
-           case _Arg_f128:
-             return std::forward<_Visitor>(__vis)(_M_val._M_f128);
            case _Arg_ibm128:
              return std::forward<_Visitor>(__vis)(_M_val._M_ibm128);
+           case _Arg_ieee128:
+             return std::forward<_Visitor>(__vis)(_M_val._M_ieee128);
#endif
+#if defined(__STDCPP_BFLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
+           case _Arg_bf16:
+             return std::forward<_Visitor>(__vis)(_M_val._M_bf16);
+#endif
+#if defined(__STDCPP_FLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
+           case _Arg_f16:
+             return std::forward<_Visitor>(__vis)(_M_val._M_f16);
+#endif
+#if defined(__FLT32_DIG__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
+           case _Arg_f32:
+             return std::forward<_Visitor>(__vis)(_M_val._M_f32);
#endif
+#if defined(__FLT64_DIG__) && defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY64)
+           case _Arg_f64:
+             return std::forward<_Visitor>(__vis)(_M_val._M_f64);
+#endif
+#endif // __glibcxx_to_chars
            case _Arg_str:
              return std::forward<_Visitor>(__vis)(_M_val._M_str);
            case _Arg_sv:
@@ -4320,14 +4402,7 @@ namespace __format
            case _Arg_u128:
              return std::forward<_Visitor>(__vis)(_M_val._M_u128);
#endif
-
-#if _GLIBCXX_FORMAT_F128 == 2
-           case _Arg_f128:
-             return std::forward<_Visitor>(__vis)(_M_val._M_f128);
-#endif
-
            default:
-             // _Arg_f16 etc.
              __builtin_unreachable();
          }
        }
diff --git a/libstdc++-v3/testsuite/std/format/arguments/args.cc 
b/libstdc++-v3/testsuite/std/format/arguments/args.cc
index 4c50bc74319..60296753919 100644
--- a/libstdc++-v3/testsuite/std/format/arguments/args.cc
+++ b/libstdc++-v3/testsuite/std/format/arguments/args.cc
@@ -164,24 +164,6 @@ void test_visited_as_handle()
#endif
}

-template<typename E, typename S>
-void test_visited_as()
-{
-  auto v = static_cast<S>(1.0);
-  auto store = std::make_format_args(v);
-  std::format_args args = store;
-
-  auto is_expected_val = [v](auto arg) {
-    if constexpr (std::is_same_v<decltype(arg), E>)
-      return arg == static_cast<E>(v);
-    return false;
-  };
-  VERIFY( std::visit_format_arg(is_expected_val, args.get(0)) );
-#if __cpp_lib_format >= 202306L // C++26 adds std::basic_format_arg::visit
-  VERIFY( args.get(0).visit(is_expected_val) );
-#endif
-}
-
template<typename T>
concept can_format = std::is_default_constructible_v<std::formatter<T, char>>;

@@ -195,30 +177,31 @@ int main()
  test_visited_as_handle<__int128>();
  test_visited_as_handle<unsigned __int128>();
#endif
-// TODO: This should be visited as handle.
-#ifdef __STDCPP_FLOAT16_T__
-  if constexpr (can_format<_Float16>)
-    test_visited_as<float, _Float16>();
-#endif
-#ifdef __STDCPP_BFLOAT16_T__
+#ifdef __BFLT16_DIG__
  if constexpr (can_format<__gnu_cxx::__bfloat16_t>)
-    test_visited_as<float, __gnu_cxx::__bfloat16_t>();
+    test_visited_as_handle<__gnu_cxx::__bfloat16_t>();
+#endif
+#ifdef __FLT16_DIG__
+  if constexpr (can_format<_Float16>)
+    test_visited_as_handle<_Float16>();
#endif
#ifdef __FLT32_DIG__
  if constexpr (can_format<_Float32>)
-    test_visited_as<float, _Float32>();
+    test_visited_as_handle<_Float32>();
#endif
#ifdef __FLT64_DIG__
  if constexpr (can_format<_Float64>)
-    test_visited_as<double, _Float64>();
+    test_visited_as_handle<_Float64>();
#endif
#ifdef __FLT128_DIG__
  if constexpr (can_format<_Float128>)
-# ifdef _GLIBCXX_LDOUBLE_IS_IEEE_BINARY128
-    test_visited_as<long double, _Float128>();
-# else
    test_visited_as_handle<_Float128>();
-# endif
+#endif
+#ifdef __SIZEOF_FLOAT128__
+  //  __ieee128 is same type as __float128, and may be long double
+  if constexpr (!std::is_same_v<__float128, long double>)
+    if constexpr (can_format<__float128>)
+      test_visited_as_handle<__float128>();
#endif
#ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT
  if constexpr (!std::is_same_v<__ieee128, long double>)
diff --git a/libstdc++-v3/testsuite/std/format/parse_ctx.cc 
b/libstdc++-v3/testsuite/std/format/parse_ctx.cc
index b5dd7cdba78..adafc58c183 100644
--- a/libstdc++-v3/testsuite/std/format/parse_ctx.cc
+++ b/libstdc++-v3/testsuite/std/format/parse_ctx.cc
@@ -443,6 +443,8 @@ test_custom()
}

#if __cpp_lib_format >= 202305
+#include <stdfloat>
+
struct X { };

template<>
@@ -458,13 +460,20 @@ struct std::formatter<X, char>
    if (spec == "int")
    {
      pc.check_dynamic_spec_integral(pc.next_arg_id());
-      integer = true;
+      type = Type::integral;
    }
    else if (spec == "str")
    {
      pc.check_dynamic_spec_string(pc.next_arg_id());
-      integer = false;
+      type = Type::string;
+    }
+    else if (spec == "float")
+    {
+      pc.check_dynamic_spec<float, double, long double>(pc.next_arg_id());
+      type = Type::floating;
    }
+    else if (spec == "other")
+      type = Type::other;
    else
      throw std::format_error("invalid format-spec");
    return pc.begin() + spec.size();
@@ -474,13 +483,44 @@ struct std::formatter<X, char>
  format(X, std::format_context& c) const
  {
    std::visit_format_arg([this]<typename T>(T) { // { dg-warning "deprecated" 
"" { target c++26 } }
-      if (is_integral_v<T> != this->integer)
-       throw std::format_error("invalid argument type");
+      constexpr bool is_handle
+       = std::is_same_v<std::basic_format_arg<std::format_context>::handle, T>;
+      constexpr bool is_integral
+       = std::is_same_v<int, T> || std::is_same_v<unsigned int, T>
+           || is_same_v<long long, T> || std::is_same_v<unsigned long long, T>;
+      constexpr bool is_string
+       = std::is_same_v<const char*, T> || std::is_same_v<std::string_view, T>;
+      constexpr bool is_floating
+       = std::is_same_v<float, T> || std::is_same_v<double, T>
+           || std::is_same_v<long double, T>;
+      switch (this->type)
+      {
+       case Type::other:
+         if (is_handle) return;
+         break;
+       case Type::integral:
+         if (is_integral) return;
+         break;
+       case Type::string:
+         if (is_string) return;
+         break;
+       case Type::floating:
+         if (is_floating) return;
+         break;
+      }
+      throw std::format_error("invalid argument type");
    }, c.arg(1));
    return c.out();
  }
private:
-  bool integer = false;
+  enum class Type
+  {
+    other,
+    integral,
+    string,
+    floating,
+  };
+  Type type = Type::other;
};
#endif

@@ -497,6 +537,28 @@ test_dynamic_type_check()

  (void) std::format("{:int}", X{}, 42L);
  (void) std::format("{:str}", X{}, "H2G2");
+  (void) std::format("{:float}", X{}, 10.0);
+
+#ifdef __STDCPP_FLOAT16_T__
+  if constexpr (std::formattable<std::bfloat16_t, char>)
+    (void) std::format("{:other}", X{}, 10.0bf16);
+#endif
+#ifdef __STDCPP_FLOAT16_T__
+  if constexpr (std::formattable<std::float16_t, char>)
+    (void) std::format("{:other}", X{}, 10.0f16);
+#endif
+#ifdef __STDCPP_FLOAT32_T__
+  if constexpr (std::formattable<std::float32_t, char>)
+    (void) std::format("{:other}", X{}, 10.0f32);
+#endif
+#ifdef __STDCPP_FLOAT64_T__
+  if constexpr (std::formattable<std::float64_t, char>)
+    (void) std::format("{:other}", X{}, 10.0f64);
+#endif
+#ifdef __STDCPP_FLOAT128_T__
+  if constexpr (std::formattable<std::float128_t, char>)
+    (void) std::format("{:other}", X{}, 10.0f128);
+#endif
#endif
}

--
2.49.0



Reply via email to