@@ -1468,8 +1468,9 @@ namespace __format
// We can format a floating-point type iff it is usable with to_chars.
template<typename _Tp>
- concept __formattable_float = requires (_Tp __t, char* __p)
- { __format::to_chars(__p, __p, __t, chars_format::scientific, 6); };
+ concept __formattable_float
+ = is_same_v<remove_cv_t<_Tp>, _Tp> && requires (_Tp __t, char* __p)
+ { __format::to_chars(__p, __p, __t, chars_format::scientific, 6); };
template<__char _CharT>
struct __formatter_fp
@@ -2184,53 +2185,53 @@ namespace __format
#endif // USE_WCHAR_T
/// @}
- /// Format an integer.
- template<integral _Tp, __format::__char _CharT>
- requires (!__is_one_of<_Tp, char, wchar_t, char16_t, char32_t>::value)
- struct formatter<_Tp, _CharT>
- {
- formatter() = default;
+/// @cond undocumented
+namespace __format
+{
+ // each cv-unqualified arithmetic type ArithmeticT other than
+ // char, wchar_t, char8_t, char16_t, or char32_t
+ template<typename _Tp>
+ constexpr bool __is_formattable_integer = __is_integer<_Tp>::__value;
- [[__gnu__::__always_inline__]]
- constexpr typename basic_format_parse_context<_CharT>::iterator
- parse(basic_format_parse_context<_CharT>& __pc)
- {
- return _M_f.template _M_parse<_Tp>(__pc);
- }
-
- template<typename _Out>
- typename basic_format_context<_Out, _CharT>::iterator
- format(_Tp __u, basic_format_context<_Out, _CharT>& __fc) const
- { return _M_f.format(__u, __fc); }
-
- private:
- __format::__formatter_int<_CharT> _M_f;
- };
-
-#if defined __SIZEOF_INT128__ && defined __STRICT_ANSI__
- template<typename _Tp, __format::__char _CharT>
- requires (__is_one_of<_Tp, __int128, unsigned __int128>::value)
- struct formatter<_Tp, _CharT>
- {
- formatter() = default;
-
- [[__gnu__::__always_inline__]]
- constexpr typename basic_format_parse_context<_CharT>::iterator
- parse(basic_format_parse_context<_CharT>& __pc)
- {
- return _M_f.template _M_parse<_Tp>(__pc);
- }
-
- template<typename _Out>
- typename basic_format_context<_Out, _CharT>::iterator
- format(_Tp __u, basic_format_context<_Out, _CharT>& __fc) const
- { return _M_f.format(__u, __fc); }
-
- private:
- __format::__formatter_int<_CharT> _M_f;
- };
+#if defined __SIZEOF_INT128__
+ template<> inline constexpr bool __is_formattable_integer<__int128> = true;
+ template<> inline constexpr bool __is_formattable_integer<unsigned __int128>
+ = true;
#endif
+ template<> inline constexpr bool __is_formattable_integer<char> = false;
+ template<> inline constexpr bool __is_formattable_integer<wchar_t> = false;
+#ifdef _GLIBCXX_USE_CHAR8_T
+ template<> inline constexpr bool __is_formattable_integer<char8_t> = false;
+#endif
+ template<> inline constexpr bool __is_formattable_integer<char16_t> = false;
+ template<> inline constexpr bool __is_formattable_integer<char32_t> = false;
+}
+/// ~endcond
+
+ /// Format an integer.
+ template<typename _Tp, __format::__char _CharT>
+ requires __format::__is_formattable_integer<_Tp>
+ struct formatter<_Tp, _CharT>
+ {
+ formatter() = default;
+
+ [[__gnu__::__always_inline__]]
+ constexpr typename basic_format_parse_context<_CharT>::iterator
+ parse(basic_format_parse_context<_CharT>& __pc)
+ {
+ return _M_f.template _M_parse<_Tp>(__pc);
+ }
+
+ template<typename _Out>
+ typename basic_format_context<_Out, _CharT>::iterator
+ format(_Tp __u, basic_format_context<_Out, _CharT>& __fc) const
+ { return _M_f.format(__u, __fc); }
+
+ private:
+ __format::__formatter_int<_CharT> _M_f;
+ };
+
#if defined __glibcxx_to_chars
/// Format a floating-point value.
template<__format::__formattable_float _Tp, __format::__char _CharT>
@@ -2614,6 +2615,8 @@ namespace __format
} // namespace __format
/// @endcond
+// Concept std::formattable was introduced by P2286R8 "Formatting Ranges",
+// but we can't guard it with __cpp_lib_format_ranges until we define that!
#if __cplusplus > 202002L
// [format.formattable], concept formattable
template<typename _Tp, typename _CharT>
@@ -26,6 +26,16 @@ test_specializations() // [format.formatter.spec]
static_assert( std::is_copy_assignable_v<Fi> );
static_assert( std::is_move_assignable_v<Fi> );
+#ifdef _GLIBCXX_USE_CHAR8_T
+ // std::string s0 = std::format("{}", u8'a'); // error: disabled formatter
+ using Fc8 = std::format_context::formatter_type<char8_t>;
+ static_assert( ! std::is_default_constructible_v<Fc8> );
+ static_assert( ! std::is_copy_constructible_v<Fc8> );
+ static_assert( ! std::is_move_constructible_v<Fc8> );
+ static_assert( ! std::is_copy_assignable_v<Fc8> );
+ static_assert( ! std::is_move_assignable_v<Fc8> );
+#endif
+
// std::string s1 = std::format("{}", L"foo"); // error: disabled formatter
using Fw = std::format_context::formatter_type<wchar_t>;
static_assert( ! std::is_default_constructible_v<Fw> );
@@ -34,6 +44,13 @@ test_specializations() // [format.formatter.spec]
static_assert( ! std::is_copy_assignable_v<Fw> );
static_assert( ! std::is_move_assignable_v<Fw> );
+ using Fic = std::format_context::formatter_type<const int>; // disabled
+ static_assert( ! std::is_default_constructible_v<Fic> );
+ static_assert( ! std::is_copy_constructible_v<Fic> );
+ static_assert( ! std::is_move_constructible_v<Fic> );
+ static_assert( ! std::is_copy_assignable_v<Fic> );
+ static_assert( ! std::is_move_assignable_v<Fic> );
+
std::string s2 = std::format("{}", red); // OK, user-provided formatter
VERIFY( s2 == "red" );
using Fc = std::format_context::formatter_type<color>;