libstdc++: Fix P2510R3 "Formatting pointers" [PR110149]
I had intended to support the P2510R3 proposal unconditionally in C++20 mode, but I left it half implemented. The parse function supported the new extensions, but the format function didn't. This adds the missing pieces, and makes it only enabled for C++26 and non-strict modes. libstdc++-v3/ChangeLog: PR libstdc++/110149 * include/std/format (formatter<const void*, charT>::parse): Only alow 0 and P for C++26 and non-strict modes. (formatter<const void*, charT>::format): Use toupper for P type, and insert zero-fill characters for 0 option. * testsuite/std/format/functions/format.cc: Check pointer formatting. Only check P2510R3 extensions conditionally. * testsuite/std/format/parse_ctx.cc: Only check P2510R3 extensions conditionally. (cherry picked from commit 628ba410b9265dbd4278c1f1b1fadf05348adef2)
This commit is contained in:
@@ -830,7 +830,7 @@ namespace __format
|
||||
{
|
||||
if (_M_spec._M_type == _Pres_esc)
|
||||
{
|
||||
// TODO: C++20 escaped string presentation
|
||||
// TODO: C++23 escaped string presentation
|
||||
}
|
||||
|
||||
if (_M_spec._M_width_kind == _WP_none
|
||||
@@ -2081,19 +2081,31 @@ namespace __format
|
||||
if (__finished())
|
||||
return __first;
|
||||
|
||||
// _GLIBCXX_RESOLVE_LIB_DEFECTS
|
||||
// P2519R3 Formatting pointers
|
||||
// _GLIBCXX_RESOLVE_LIB_DEFECTS
|
||||
// P2510R3 Formatting pointers
|
||||
#define _GLIBCXX_P2518R3 (__cplusplus > 202302L || ! defined __STRICT_ANSI__)
|
||||
|
||||
#if _GLIBCXX_P2518R3
|
||||
__first = __spec._M_parse_zero_fill(__first, __last);
|
||||
if (__finished())
|
||||
return __first;
|
||||
#endif
|
||||
|
||||
__first = __spec._M_parse_width(__first, __last, __pc);
|
||||
|
||||
if (__first != __last && (*__first == 'p' || *__first == 'P'))
|
||||
if (__first != __last)
|
||||
{
|
||||
if (*__first == 'P')
|
||||
if (*__first == 'p')
|
||||
++__first;
|
||||
#if _GLIBCXX_P2518R3
|
||||
else if (*__first == 'P')
|
||||
{
|
||||
// _GLIBCXX_RESOLVE_LIB_DEFECTS
|
||||
// P2510R3 Formatting pointers
|
||||
__spec._M_type = __format::_Pres_P;
|
||||
++__first;
|
||||
++__first;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (__finished())
|
||||
@@ -2110,9 +2122,21 @@ namespace __format
|
||||
char __buf[2 + sizeof(__v) * 2];
|
||||
auto [__ptr, __ec] = std::to_chars(__buf + 2, std::end(__buf),
|
||||
__u, 16);
|
||||
const int __n = __ptr - __buf;
|
||||
int __n = __ptr - __buf;
|
||||
__buf[0] = '0';
|
||||
__buf[1] = 'x';
|
||||
#if _GLIBCXX_P2518R3
|
||||
if (_M_spec._M_type == __format::_Pres_P)
|
||||
{
|
||||
__buf[1] = 'X';
|
||||
for (auto __p = __buf + 2; __p != __ptr; ++__p)
|
||||
#if __has_builtin(__builtin_toupper)
|
||||
*__p = __builtin_toupper(*__p);
|
||||
#else
|
||||
*__p = std::toupper(*__p);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
basic_string_view<_CharT> __str;
|
||||
if constexpr (is_same_v<_CharT, char>)
|
||||
@@ -2126,6 +2150,24 @@ namespace __format
|
||||
__str = wstring_view(__p, __n);
|
||||
}
|
||||
|
||||
#if _GLIBCXX_P2518R3
|
||||
if (_M_spec._M_zero_fill)
|
||||
{
|
||||
size_t __width = _M_spec._M_get_width(__fc);
|
||||
if (__width <= __str.size())
|
||||
return __format::__write(__fc.out(), __str);
|
||||
|
||||
auto __out = __fc.out();
|
||||
// Write "0x" or "0X" prefix before zero-filling.
|
||||
__out = __format::__write(std::move(__out), __str.substr(0, 2));
|
||||
__str.remove_prefix(2);
|
||||
size_t __nfill = __width - __n;
|
||||
return __format::__write_padded(std::move(__out), __str,
|
||||
__format::_Align_right,
|
||||
__nfill, _CharT('0'));
|
||||
}
|
||||
#endif
|
||||
|
||||
return __format::__write_padded_as_spec(__str, __n, __fc, _M_spec,
|
||||
__format::_Align_right);
|
||||
}
|
||||
|
||||
@@ -206,6 +206,8 @@ test_width()
|
||||
VERIFY( s == " " );
|
||||
s = std::format("{:{}}", "", 3);
|
||||
VERIFY( s == " " );
|
||||
s = std::format("{:{}}|{:{}}", 1, 2, 3, 4);
|
||||
VERIFY( s == " 1| 3" );
|
||||
s = std::format("{1:{0}}", 2, "");
|
||||
VERIFY( s == " " );
|
||||
s = std::format("{:03}", 9);
|
||||
@@ -342,6 +344,45 @@ test_float128()
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
test_pointer()
|
||||
{
|
||||
void* p = nullptr;
|
||||
const void* pc = p;
|
||||
std::string s, str_int;
|
||||
|
||||
s = std::format("{} {} {}", p, pc, nullptr);
|
||||
VERIFY( s == "0x0 0x0 0x0" );
|
||||
s = std::format("{:p} {:p} {:p}", p, pc, nullptr);
|
||||
VERIFY( s == "0x0 0x0 0x0" );
|
||||
s = std::format("{:4},{:5},{:6}", p, pc, nullptr); // width
|
||||
VERIFY( s == " 0x0, 0x0, 0x0" );
|
||||
s = std::format("{:<4},{:>5},{:^7}", p, pc, nullptr); // align+width
|
||||
VERIFY( s == "0x0 , 0x0, 0x0 " );
|
||||
s = std::format("{:o<4},{:o>5},{:o^7}", p, pc, nullptr); // fill+align+width
|
||||
VERIFY( s == "0x0o,oo0x0,oo0x0oo" );
|
||||
|
||||
pc = p = &s;
|
||||
str_int = std::format("{:#x}", reinterpret_cast<std::uintptr_t>(p));
|
||||
s = std::format("{} {} {}", p, pc, nullptr);
|
||||
VERIFY( s == (str_int + ' ' + str_int + " 0x0") );
|
||||
str_int = std::format("{:#20x}", reinterpret_cast<std::uintptr_t>(p));
|
||||
s = std::format("{:20} {:20p}", p, pc);
|
||||
VERIFY( s == (str_int + ' ' + str_int) );
|
||||
|
||||
#if __cplusplus > 202302L || ! defined __STRICT_ANSI__
|
||||
// P2510R3 Formatting pointers
|
||||
s = std::format("{:06} {:07P} {:08p}", (void*)0, (const void*)0, nullptr);
|
||||
VERIFY( s == "0x0000 0X00000 0x000000" );
|
||||
str_int = std::format("{:#016x}", reinterpret_cast<std::uintptr_t>(p));
|
||||
s = std::format("{:016} {:016}", p, pc);
|
||||
VERIFY( s == (str_int + ' ' + str_int) );
|
||||
str_int = std::format("{:#016X}", reinterpret_cast<std::uintptr_t>(p));
|
||||
s = std::format("{:016P} {:016P}", p, pc);
|
||||
VERIFY( s == (str_int + ' ' + str_int) );
|
||||
#endif
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_no_args();
|
||||
@@ -354,4 +395,5 @@ int main()
|
||||
test_minmax();
|
||||
test_p1652r1();
|
||||
test_float128();
|
||||
test_pointer();
|
||||
}
|
||||
|
||||
@@ -244,10 +244,6 @@ test_pointer()
|
||||
VERIFY( ! is_std_format_spec_for<void*>("-") );
|
||||
VERIFY( ! is_std_format_spec_for<void*>(" ") );
|
||||
VERIFY( ! is_std_format_spec_for<void*>("#") );
|
||||
VERIFY( is_std_format_spec_for<void*>("0p") ); // P2510
|
||||
VERIFY( is_std_format_spec_for<void*>("0") );
|
||||
VERIFY( ! is_std_format_spec_for<void*>("00p") );
|
||||
VERIFY( is_std_format_spec_for<void*>("01p") );
|
||||
VERIFY( is_std_format_spec_for<void*>("1") );
|
||||
VERIFY( ! is_std_format_spec_for<void*>("-1") );
|
||||
VERIFY( ! is_std_format_spec_for<void*>("-1p") );
|
||||
@@ -263,7 +259,6 @@ test_pointer()
|
||||
VERIFY( ! is_std_format_spec_for<void*>("s") );
|
||||
VERIFY( ! is_std_format_spec_for<void*>("?") );
|
||||
VERIFY( is_std_format_spec_for<void*>("p") );
|
||||
VERIFY( is_std_format_spec_for<void*>("P") );
|
||||
VERIFY( ! is_std_format_spec_for<void*>("a") );
|
||||
VERIFY( ! is_std_format_spec_for<void*>("A") );
|
||||
VERIFY( ! is_std_format_spec_for<void*>("f") );
|
||||
@@ -271,6 +266,16 @@ test_pointer()
|
||||
VERIFY( ! is_std_format_spec_for<void*>("g") );
|
||||
VERIFY( ! is_std_format_spec_for<void*>("G") );
|
||||
VERIFY( ! is_std_format_spec_for<void*>("+p") );
|
||||
|
||||
#if __cplusplus > 202302L || ! defined __STRICT_ANSI__
|
||||
// As an extension, we support P2510R3 Formatting pointers
|
||||
VERIFY( is_std_format_spec_for<void*>("P") );
|
||||
VERIFY( is_std_format_spec_for<void*>("0p") );
|
||||
VERIFY( is_std_format_spec_for<void*>("0P") );
|
||||
VERIFY( is_std_format_spec_for<void*>("0") );
|
||||
VERIFY( is_std_format_spec_for<void*>("01p") );
|
||||
VERIFY( ! is_std_format_spec_for<void*>("00p") );
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
Reference in New Issue
Block a user