Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 50 additions & 17 deletions stl/inc/format
Original file line number Diff line number Diff line change
Expand Up @@ -1762,10 +1762,13 @@ template <class _Handler>
class _Specs_checker : public _Handler {
private:
_Basic_format_arg_type _Arg_type;
// we'll see this if we get a modifier that requires an integer presentation type
// for types that can have either integer or non-integer presentation types (charT or bool)

// Set for the hash and sign modifiers.
bool _Need_arithmetic_presentation_type = false;

// Set for the zero modifier.
bool _Need_arithmetic_or_pointer_presentation_type = false;

public:
constexpr explicit _Specs_checker(const _Handler& _Handler_inst, const _Basic_format_arg_type _Arg_type_)
: _Handler(_Handler_inst), _Arg_type(_Arg_type_) {}
Expand All @@ -1776,6 +1779,12 @@ public:
}
}

constexpr void _Require_numeric_or_pointer_argument() const {
if (!_Is_arithmetic_fmt_type(_Arg_type) && _Arg_type != _Basic_format_arg_type::_Pointer_type) {
_Throw_format_error("Format specifier requires numeric or pointer argument.");
}
}

constexpr void _Check_precision() const {
if (_Is_integral_fmt_type(_Arg_type) || _Arg_type == _Basic_format_arg_type::_Pointer_type) {
_Throw_format_error("Precision not allowed for this argument type.");
Expand All @@ -1802,8 +1811,8 @@ public:
}

constexpr void _On_zero() {
_Need_arithmetic_presentation_type = true;
_Require_numeric_argument();
_Need_arithmetic_or_pointer_presentation_type = true;
_Require_numeric_or_pointer_argument();
_Handler::_On_zero();
}

Expand Down Expand Up @@ -1869,6 +1878,7 @@ public:
_Cat = _Presentation_type_category::_Floating;
break;
case 'p':
case 'P':
_Cat = _Presentation_type_category::_Pointer;
break;
default:
Expand Down Expand Up @@ -1960,7 +1970,18 @@ public:

if (_Need_arithmetic_presentation_type && _Cat != _Presentation_type_category::_Integer
&& _Cat != _Presentation_type_category::_Floating) {
_Throw_format_error("Modifier requires an integer presentation type for bool");
// N4971 [format.string.std]/5: "The sign option is only valid for arithmetic types
// other than charT and bool or when an integer presentation type is specified."
// N4971 [format.string.std]/7: "The # option [...] is valid for arithmetic types
// other than charT and bool or when an integer presentation type is specified, and not otherwise."
_Throw_format_error("Hash/sign modifier requires an arithmetic presentation type");
}

if (_Need_arithmetic_or_pointer_presentation_type && _Cat != _Presentation_type_category::_Integer
&& _Cat != _Presentation_type_category::_Floating && _Cat != _Presentation_type_category::_Pointer) {
// N4971 [format.string.std]/8: "The 0 option is valid for arithmetic types
// other than charT and bool, pointer types, or when an integer presentation type is specified."
_Throw_format_error("Zero modifier requires an arithmetic or pointer presentation type");
}
_Handler::_On_type(_Type);
}
Expand Down Expand Up @@ -3168,25 +3189,37 @@ _NODISCARD _OutputIt _Fmt_write(
template <class _CharT, class _OutputIt>
_NODISCARD _OutputIt _Fmt_write(
_OutputIt _Out, const void* const _Value, const _Basic_format_specs<_CharT>& _Specs, _Lazy_locale) {
_STL_INTERNAL_CHECK(_Specs._Type == '\0' || _Specs._Type == 'p');
_STL_INTERNAL_CHECK(_Specs._Type == '\0' || _Specs._Type == 'p' || _Specs._Type == 'P');
_STL_INTERNAL_CHECK(_Specs._Sgn == _Fmt_sign::_None);
_STL_INTERNAL_CHECK(!_Specs._Alt);
_STL_INTERNAL_CHECK(_Specs._Precision == -1);
_STL_INTERNAL_CHECK(!_Specs._Leading_zero);
_STL_INTERNAL_CHECK(!_Specs._Localized);

// Since the bit width of 0 is 0x0, special-case it instead of complicating the math even more.
int _Width = 3;
if (_Value != nullptr) {
// Compute the bit width of the pointer (i.e. how many bits it takes to be represented).
// Add 3 to the bit width so we always round up on the division.
// Divide that by the amount of bits a hexit represents (log2(16) = log2(2^4) = 4).
// Add 2 for the 0x prefix.
_Width = 2 + (_STD bit_width(reinterpret_cast<uintptr_t>(_Value)) + 3) / 4;
char _Buffer[2 * sizeof(void*)]; // 2 hexits per byte: 8 hexits for 32-bit, 16 hexits for 64-bit
const auto [_End, _Ec] = _STD to_chars(_Buffer, _STD end(_Buffer), reinterpret_cast<uintptr_t>(_Value), 16);
_STL_INTERNAL_CHECK(_Ec == errc{});
const int _Width = 2 + static_cast<int>(_End - _Buffer);

_CharT _Prefix[2] = {_CharT{'0'}, _CharT{'x'}};
if (_Specs._Type == 'P') {
_Prefix[1] = _CharT{'X'};
_Buffer_to_uppercase(_Buffer, _End);
}

const bool _Write_leading_zeroes = _Specs._Leading_zero && _Specs._Alignment == _Fmt_align::_None;
auto _Writer = [&](_OutputIt _Out) {
_Out = _RANGES copy(_Prefix, _Prefix + 2, _STD move(_Out)).out;
if (_Write_leading_zeroes && _Width < _Specs._Width) {
_Out = _RANGES fill_n(_STD move(_Out), _Specs._Width - _Width, _CharT{'0'});
}
return _STD _Widen_and_copy<_CharT>(_Buffer, _End, _STD move(_Out));
};

if (_Write_leading_zeroes) {
return _Writer(_STD move(_Out));
}

return _Write_aligned(_STD move(_Out), _Width, _Specs, _Fmt_align::_Right,
[=](_OutputIt _Out) { return _Fmt_write<_CharT>(_STD move(_Out), _Value); });
return _STD _Write_aligned(_STD move(_Out), _Width, _Specs, _Fmt_align::_Right, _Writer);
}

template <class _CharT, class _OutputIt>
Expand Down
3 changes: 2 additions & 1 deletion stl/inc/yvals_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@
// P2432R1 Fix istream_view
// P2465R3 Standard Library Modules std And std.compat
// P2508R1 basic_format_string, format_string, wformat_string
// P2510R3 Formatting Pointers
// P2520R0 move_iterator<T*> Should Be A Random-Access Iterator
// P2538R1 ADL-Proof projected
// P2572R1 std::format Fill Character Allowances
Expand Down Expand Up @@ -1751,7 +1752,7 @@ _EMIT_STL_ERROR(STL1004, "C++98 unexpected() is incompatible with C++23 unexpect
#define __cpp_lib_erase_if 202002L

#ifdef __cpp_lib_concepts
#define __cpp_lib_format 202207L
#define __cpp_lib_format 202304L
#define __cpp_lib_format_uchar 202311L
#define __cpp_lib_freestanding_ranges 202306L
#endif // defined(__cpp_lib_concepts)
Expand Down
6 changes: 0 additions & 6 deletions tests/libcxx/expected_results.txt
Original file line number Diff line number Diff line change
Expand Up @@ -294,12 +294,6 @@ std/language.support/support.limits/support.limits.general/charconv.version.comp
std/utilities/charconv/charconv.syn/from_chars_result.operator_bool.pass.cpp FAIL
std/utilities/charconv/charconv.syn/to_chars_result.operator_bool.pass.cpp FAIL

# P2510R3 Formatting Pointers
std/utilities/format/format.functions/format.locale.pass.cpp FAIL
std/utilities/format/format.functions/format.pass.cpp FAIL
std/utilities/format/format.functions/vformat.locale.pass.cpp FAIL
std/utilities/format/format.functions/vformat.pass.cpp FAIL

# P2587R3 Redefining to_string To Use to_chars
std/language.support/support.limits/support.limits.general/string.version.compile.pass.cpp FAIL

Expand Down
1 change: 1 addition & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,7 @@ tests\P2474R2_views_repeat
tests\P2474R2_views_repeat_death
tests\P2494R2_move_only_range_adaptors
tests\P2505R5_monadic_functions_for_std_expected
tests\P2510R3_text_formatting_pointers
tests\P2517R1_apply_conditional_noexcept
tests\P2538R1_adl_proof_std_projected
tests\P2609R3_relaxing_ranges_just_a_smidge
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -991,7 +991,7 @@ void test_pointer_specs() {
throw_helper(STR("{:#}"), nullptr);

// Leading zero
throw_helper(STR("{:0}"), nullptr);
assert(format(STR("{:05}"), nullptr) == STR("0x000"));

// Width
assert(format(STR("{:5}"), nullptr) == STR(" 0x0"));
Expand Down Expand Up @@ -1350,10 +1350,10 @@ void libfmt_formatter_test_zero_flag() {
assert(format(STR("{0:05}"), 42ull) == STR("00042"));
assert(format(STR("{0:07}"), -42.0) == STR("-000042"));
assert(format(STR("{0:07}"), -42.0l) == STR("-000042"));
assert(format(STR("{0:05}"), reinterpret_cast<void*>(0x42)) == STR("0x042"));
throw_helper(STR("{0:0"), 'c');
throw_helper(STR("{0:05}"), 'c');
throw_helper(STR("{0:05}"), STR("abc"));
throw_helper(STR("{0:05}"), reinterpret_cast<void*>(0x42));
}

template <class charT>
Expand Down
4 changes: 4 additions & 0 deletions tests/std/tests/P2510R3_text_formatting_pointers/env.lst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\concepts_20_matrix.lst
Loading