Truncate "(null)" to the precision and print null pointers as 0x0

Printing nothing for a small precision hides the null, and other libc
implementations truncate too. 0x0 matches fmt::format and std::format.
This commit is contained in:
Victor Zverovich 2026-11-07 18:57:33 -08:00
parent 1b5222af1b
commit d102a01edf
2 changed files with 13 additions and 28 deletions

View File

@ -236,12 +236,6 @@ class printf_arg_formatter : public arg_formatter<Char> {
context_type& context_;
void write_null_pointer(bool is_string = false) {
auto s = this->specs;
s.set_type(presentation_type::none);
write_bytes<Char>(this->out, is_string ? "(null)" : "(nil)", s);
}
template <typename T> void write(T value) {
detail::write<Char>(this->out, value, this->specs, this->locale);
}
@ -292,27 +286,20 @@ class printf_arg_formatter : public arg_formatter<Char> {
}
void operator()(const char* value) {
if (value)
write(value);
else
write_null_pointer(this->specs.type() != presentation_type::pointer);
if (!value && this->specs.type() != presentation_type::pointer)
value = "(null)";
write(value);
}
void operator()(const wchar_t* value) {
if (value)
write(value);
else
write_null_pointer(this->specs.type() != presentation_type::pointer);
if (!value && this->specs.type() != presentation_type::pointer)
value = L"(null)";
write(value);
}
void operator()(basic_string_view<Char> value) { write(value); }
void operator()(const void* value) {
if (value)
write(value);
else
write_null_pointer();
}
void operator()(const void* value) { write(value); }
void operator()(typename basic_format_arg<context_type>::handle handle) {
auto parse_ctx = parse_context<Char>({});
@ -500,9 +487,6 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
auto sv = basic_string_view<Char>(
str, to_unsigned(nul != str_end ? nul - str : specs.precision));
arg = sv;
} else if (specs.precision < 6) {
// Like glibc, print "(null)" only if it fits in the precision.
arg = basic_string_view<Char>();
}
}
if (specs.fill_unit<Char>() == '0') {

View File

@ -544,10 +544,11 @@ TEST(printf_test, string) {
const char* null_str = nullptr;
EXPECT_PRINTF("(null)", "%s", null_str);
EXPECT_PRINTF(" (null)", "%10s", null_str);
// Like glibc, a precision too small for "(null)" gives an empty string.
// A precision truncates "(null)" as it would any other string.
EXPECT_PRINTF("", "%.0s", null_str);
EXPECT_PRINTF("", "%.3s", null_str);
EXPECT_PRINTF("(nu", "%.3s", null_str);
EXPECT_PRINTF("(null)", "%.6s", null_str);
EXPECT_PRINTF(" (nu", "%10.3s", null_str);
}
TEST(printf_test, pointer) {
@ -555,12 +556,12 @@ TEST(printf_test, pointer) {
void* p = &n;
EXPECT_PRINTF(fmt::format("{}", p), "%p", p);
p = nullptr;
EXPECT_PRINTF("(nil)", "%p", p);
EXPECT_PRINTF(" (nil)", "%10p", p);
EXPECT_PRINTF("0x0", "%p", p);
EXPECT_PRINTF(" 0x0", "%10p", p);
const char* s = "test";
EXPECT_PRINTF(fmt::format("{:p}", s), "%p", s);
const char* null_str = nullptr;
EXPECT_PRINTF("(nil)", "%p", null_str);
EXPECT_PRINTF("0x0", "%p", null_str);
}
enum test_enum { answer = 42 };