Skip to content

Commit e0b8a30

Browse files
Implement range-default-formatter
The following tests are derived from libc++'s test files: - `P2286R8_text_formatting_range_map` - `P2286R8_text_formatting_range_sequence` - `P2286R8_text_formatting_range_set` - `P2286R8_text_formatting_range_string` Note that libc++'s `range_formatter` is currently buggy on the `m` option, see LLVM-90196 and LLVM-94562. The test `P2286R8_text_formatting_tuple_disambiguation` covers formatting `pair`s and `tuple`s that are also ranges. Unblocks libcxx tests: - `std/containers/sequences/vector.bool/vector.bool.fmt/format.functions.format.pass.cpp` - `std/containers/sequences/vector.bool/vector.bool.fmt/format.functions.vformat.pass.cpp` - `std/utilities/format/format.range/format.range.fmtdef/parse.pass.cpp` - `std/utilities/format/format.range/format.range.fmtmap/format.functions.format.pass.cpp` - `std/utilities/format/format.range/format.range.fmtmap/format.functions.vformat.pass.cpp` - `std/utilities/format/format.range/format.range.fmtmap/parse.pass.cpp` - `std/utilities/format/format.range/format.range.fmtset/parse.pass.cpp` - `std/utilities/format/format.range/format.range.fmtstr/format.functions.format.pass.cpp` - `std/utilities/format/format.range/format.range.fmtstr/format.functions.vformat.pass.cpp` - `std/utilities/format/format.range/format.range.fmtstr/parse.pass.cpp` - `std/input.output/iostream.format/print.fun/includes.compile.pass.cpp`
1 parent e6d9c83 commit e0b8a30

File tree

15 files changed

+5801
-23
lines changed

15 files changed

+5801
-23
lines changed

stl/inc/__msvc_formatter.hpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@
4747
#include <cstddef>
4848
#include <cstdint>
4949
#include <type_traits>
50+
#if _HAS_CXX23
51+
#include <xutility>
52+
#endif // _HAS_CXX23
5053

5154
#pragma pack(push, _CRT_PACKING)
5255
#pragma warning(push, _STL_WARNING_LEVEL)
@@ -305,11 +308,19 @@ constexpr range_format format_kind<_Rng> = []() consteval {
305308
}
306309
}();
307310

308-
// Specializations for pairs and tuples are forward-declared to avoid any risk of using the disabled primary template.
311+
// Specializations for pairs, tuples and ranges are forward-declared to avoid any risk of using the disabled primary
312+
// template.
309313

310314
// Per LWG-3997, `_CharT` in library-provided `formatter` specializations is
311315
// constrained to character types supported by `format`.
312316

317+
template <class _Rng>
318+
concept _Formatting_enabled_range = format_kind<_Rng> != range_format::disabled;
319+
320+
template <_RANGES input_range _Rng, _Format_supported_charT _CharT>
321+
requires _Formatting_enabled_range<_Rng>
322+
struct formatter<_Rng, _CharT>;
323+
313324
_EXPORT_STD template <class, class>
314325
struct pair;
315326

stl/inc/format

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4247,6 +4247,150 @@ private:
42474247
}
42484248
};
42494249

4250+
template <class _Rng, class _CharT>
4251+
concept _Const_formattable_range =
4252+
_RANGES input_range<const _Rng> && formattable<_RANGES range_reference_t<const _Rng>, _CharT>;
4253+
4254+
template <class _Rng, class _CharT>
4255+
using _Fmt_maybe_const = conditional_t<_Const_formattable_range<_Rng, _CharT>, const _Rng, _Rng>;
4256+
4257+
template <range_format _Kind, class _Rng, class _CharT>
4258+
struct _Range_default_formatter;
4259+
4260+
template <_RANGES input_range _Rng, class _CharT>
4261+
struct _Range_default_formatter<range_format::sequence, _Rng, _CharT> {
4262+
private:
4263+
using _Range_type = _Fmt_maybe_const<_Rng, _CharT>;
4264+
4265+
range_formatter<remove_cvref_t<_RANGES range_reference_t<_Range_type>>, _CharT> _Underlying;
4266+
4267+
public:
4268+
constexpr void set_separator(const basic_string_view<_CharT> _Sep) noexcept {
4269+
_Underlying.set_separator(_Sep);
4270+
}
4271+
4272+
constexpr void set_brackets(
4273+
const basic_string_view<_CharT> _Opening, const basic_string_view<_CharT> _Closing) noexcept {
4274+
_Underlying.set_brackets(_Opening, _Closing);
4275+
}
4276+
4277+
template <class _ParseContext>
4278+
constexpr _ParseContext::iterator parse(_ParseContext& _Ctx) {
4279+
return _Underlying.parse(_Ctx);
4280+
}
4281+
4282+
template <class _FormatContext>
4283+
_FormatContext::iterator format(_Range_type& _Elems, _FormatContext& _Ctx) const {
4284+
return _Underlying.format(_Elems, _Ctx);
4285+
}
4286+
};
4287+
4288+
template <_RANGES input_range _Rng, class _CharT>
4289+
struct _Range_default_formatter<range_format::map, _Rng, _CharT> {
4290+
private:
4291+
using _Map_type = _Fmt_maybe_const<_Rng, _CharT>;
4292+
using _Element_type = remove_cvref_t<_RANGES range_reference_t<_Map_type>>;
4293+
4294+
range_formatter<_Element_type, _CharT> _Underlying;
4295+
4296+
public:
4297+
constexpr _Range_default_formatter() noexcept(
4298+
is_nothrow_default_constructible_v<range_formatter<_Element_type, _CharT>>) /* strengthened */ {
4299+
static_assert(_Is_two_tuple<_Element_type>, "the element type of the formatted range must be either pair<T, U> "
4300+
"or tuple<T, U> (N4981 [format.range.fmtmap]/1)");
4301+
4302+
_Underlying.set_brackets(_STATICALLY_WIDEN(_CharT, "{"), _STATICALLY_WIDEN(_CharT, "}"));
4303+
_Underlying.underlying().set_brackets({}, {});
4304+
_Underlying.underlying().set_separator(_STATICALLY_WIDEN(_CharT, ": "));
4305+
}
4306+
4307+
template <class _ParseContext>
4308+
constexpr _ParseContext::iterator parse(_ParseContext& _Ctx) {
4309+
return _Underlying.parse(_Ctx);
4310+
}
4311+
4312+
template <class _FormatContext>
4313+
_FormatContext::iterator format(_Map_type& _Rx, _FormatContext& _Ctx) const {
4314+
return _Underlying.format(_Rx, _Ctx);
4315+
}
4316+
};
4317+
4318+
template <_RANGES input_range _Rng, class _CharT>
4319+
struct _Range_default_formatter<range_format::set, _Rng, _CharT> {
4320+
private:
4321+
using _Set_type = _Fmt_maybe_const<_Rng, _CharT>; // exposition only
4322+
range_formatter<remove_cvref_t<_RANGES range_reference_t<_Set_type>>, _CharT> _Underlying;
4323+
4324+
public:
4325+
constexpr _Range_default_formatter() noexcept(is_nothrow_default_constructible_v<
4326+
range_formatter<remove_cvref_t<_RANGES range_reference_t<_Set_type>>, _CharT>>) /* strengthened */ {
4327+
_Underlying.set_brackets(_STATICALLY_WIDEN(_CharT, "{"), _STATICALLY_WIDEN(_CharT, "}"));
4328+
}
4329+
4330+
template <class _ParseContext>
4331+
constexpr _ParseContext::iterator parse(_ParseContext& _Ctx) {
4332+
return _Underlying.parse(_Ctx);
4333+
}
4334+
4335+
template <class _FormatContext>
4336+
_FormatContext::iterator format(_Set_type& _Rx, _FormatContext& _Ctx) const {
4337+
return _Underlying.format(_Rx, _Ctx);
4338+
}
4339+
};
4340+
4341+
template <range_format _Kind, _RANGES input_range _Rng, class _CharT>
4342+
requires (_Kind == range_format::string || _Kind == range_format::debug_string)
4343+
struct _Range_default_formatter<_Kind, _Rng, _CharT> {
4344+
private:
4345+
static_assert(is_same_v<remove_cvref_t<_RANGES range_reference_t<_Rng>>, _CharT>,
4346+
"the element type of the formatted range must be the character type used in formatting "
4347+
"(N4981 [format.range.fmtstr]/1)");
4348+
4349+
using _Range_type = _Maybe_const<_RANGES input_range<const _Rng>, _Rng>;
4350+
4351+
formatter<basic_string_view<_CharT>, _CharT> _Underlying; // avoid copy the string if possible
4352+
4353+
public:
4354+
template <class _ParseContext>
4355+
constexpr _ParseContext::iterator parse(_ParseContext& _Ctx) {
4356+
auto _Iter = _Underlying.parse(_Ctx);
4357+
if constexpr (_Kind == range_format::debug_string) {
4358+
_Underlying.set_debug_format();
4359+
}
4360+
return _Iter;
4361+
}
4362+
4363+
template <class _FormatContext>
4364+
_FormatContext::iterator format(_Range_type& _Rx, _FormatContext& _Ctx) const {
4365+
if constexpr (_RANGES contiguous_range<_Range_type>) {
4366+
const auto _Size = _STD _To_unsigned_like(_RANGES distance(_Rx));
4367+
4368+
if (!_STD in_range<size_t>(_Size)) [[unlikely]] {
4369+
_Throw_format_error("Formatted range is too long.");
4370+
}
4371+
4372+
const basic_string_view<_CharT> _Str(_STD to_address(_RANGES begin(_Rx)), static_cast<size_t>(_Size));
4373+
return _Underlying.format(_Str, _Ctx);
4374+
} else {
4375+
return _Underlying.format(basic_string<_CharT>{from_range, _Rx}, _Ctx);
4376+
}
4377+
}
4378+
};
4379+
4380+
// the deleted default constructor makes it "disabled" as per N4981 [format.formatter.spec]/5
4381+
4382+
template <_RANGES input_range _Rng, _Format_supported_charT _CharT>
4383+
requires _Formatting_enabled_range<_Rng>
4384+
struct formatter<_Rng, _CharT> {
4385+
formatter() = delete;
4386+
formatter(const formatter&) = delete;
4387+
formatter& operator=(const formatter&) = delete;
4388+
};
4389+
4390+
template <_RANGES input_range _Rng, _Format_supported_charT _CharT>
4391+
requires _Formatting_enabled_range<_Rng> && formattable<_RANGES range_reference_t<_Rng>, _CharT>
4392+
struct formatter<_Rng, _CharT> : _Range_default_formatter<format_kind<_Rng>, _Rng, _CharT> {};
4393+
42504394
enum class _Fmt_tuple_type : uint8_t { _None, _Key_value, _No_brackets };
42514395

42524396
template <class _CharT, bool _IsTwoTuple>
@@ -4497,6 +4641,24 @@ public:
44974641
}
44984642
};
44994643

4644+
// specializations for tuple-like types that are input ranges and not formattable as tuples
4645+
4646+
template <_Format_supported_charT _CharT, class _Ty1, class _Ty2>
4647+
requires (!formattable<_Ty1, _CharT> || !formattable<_Ty2, _CharT>)
4648+
// TRANSITION, clang-format, () should be redundant
4649+
&& (_RANGES input_range<pair<_Ty1, _Ty2>>) && _Formatting_enabled_range<pair<_Ty1, _Ty2>>
4650+
&& formattable<_RANGES range_reference_t<pair<_Ty1, _Ty2>>, _CharT>
4651+
struct _Tuple_formatter_base<pair<_Ty1, _Ty2>, _CharT>
4652+
: _Range_default_formatter<format_kind<pair<_Ty1, _Ty2>>, pair<_Ty1, _Ty2>, _CharT> {};
4653+
4654+
template <_Format_supported_charT _CharT, class... _Types>
4655+
requires ((!formattable<_Types, _CharT>) || ...)
4656+
// TRANSITION, clang-format, () should be redundant
4657+
&& (_RANGES input_range<tuple<_Types...>>) && _Formatting_enabled_range<tuple<_Types...>>
4658+
&& formattable<_RANGES range_reference_t<tuple<_Types...>>, _CharT>
4659+
struct _Tuple_formatter_base<tuple<_Types...>, _CharT>
4660+
: _Range_default_formatter<format_kind<tuple<_Types...>>, tuple<_Types...>, _CharT> {};
4661+
45004662
// Per LWG-3997, `_CharT` in library-provided `formatter` specializations is
45014663
// constrained to character types supported by `format`.
45024664

tests/libcxx/expected_results.txt

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ std/time/time.syn/formatter.year_month_weekday.pass.cpp:1 FAIL
2626
std/utilities/memory/specialized.algorithms/uninitialized.copy/uninitialized_copy.pass.cpp FAIL
2727
std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move.pass.cpp FAIL
2828

29+
# LLVM-90196: [libc++][format] Formatting range with m range-type is incorrect
30+
std/utilities/format/format.range/format.range.formatter/format.functions.format.pass.cpp FAIL
31+
std/utilities/format/format.range/format.range.formatter/format.functions.vformat.pass.cpp FAIL
32+
std/utilities/format/format.range/format.range.fmtset/format.functions.format.pass.cpp FAIL
33+
std/utilities/format/format.range/format.range.fmtset/format.functions.vformat.pass.cpp FAIL
34+
2935
# Non-Standard regex behavior.
3036
# "It seems likely that the test is still non-conforming due to how libc++ handles the 'w' character class."
3137
std/re/re.traits/lookup_classname.pass.cpp FAIL
@@ -211,29 +217,7 @@ std/containers/container.adaptors/container.adaptors.format/format.functions.vfo
211217
std/containers/container.adaptors/container.adaptors.format/format.pass.cpp FAIL
212218
std/containers/container.adaptors/container.adaptors.format/parse.pass.cpp FAIL
213219
std/containers/container.adaptors/container.adaptors.format/types.compile.pass.cpp FAIL
214-
std/containers/sequences/vector.bool/vector.bool.fmt/format.functions.format.pass.cpp FAIL
215-
std/containers/sequences/vector.bool/vector.bool.fmt/format.functions.vformat.pass.cpp FAIL
216-
std/containers/sequences/vector.bool/vector.bool.fmt/format.pass.cpp FAIL
217-
std/input.output/iostream.format/print.fun/includes.compile.pass.cpp FAIL
218220
std/utilities/format/format.formattable/concept.formattable.compile.pass.cpp FAIL
219-
std/utilities/format/format.range/format.range.fmtdef/format.pass.cpp FAIL
220-
std/utilities/format/format.range/format.range.fmtdef/parse.pass.cpp FAIL
221-
std/utilities/format/format.range/format.range.fmtdef/set_brackets.pass.cpp FAIL
222-
std/utilities/format/format.range/format.range.fmtdef/set_separator.pass.cpp FAIL
223-
std/utilities/format/format.range/format.range.fmtmap/format.functions.format.pass.cpp FAIL
224-
std/utilities/format/format.range/format.range.fmtmap/format.functions.vformat.pass.cpp FAIL
225-
std/utilities/format/format.range/format.range.fmtmap/format.pass.cpp FAIL
226-
std/utilities/format/format.range/format.range.fmtmap/parse.pass.cpp FAIL
227-
std/utilities/format/format.range/format.range.fmtset/format.functions.format.pass.cpp FAIL
228-
std/utilities/format/format.range/format.range.fmtset/format.functions.vformat.pass.cpp FAIL
229-
std/utilities/format/format.range/format.range.fmtset/format.pass.cpp FAIL
230-
std/utilities/format/format.range/format.range.fmtset/parse.pass.cpp FAIL
231-
std/utilities/format/format.range/format.range.fmtstr/format.functions.format.pass.cpp FAIL
232-
std/utilities/format/format.range/format.range.fmtstr/format.functions.vformat.pass.cpp FAIL
233-
std/utilities/format/format.range/format.range.fmtstr/format.pass.cpp FAIL
234-
std/utilities/format/format.range/format.range.fmtstr/parse.pass.cpp FAIL
235-
std/utilities/format/format.range/format.range.formatter/format.functions.format.pass.cpp FAIL
236-
std/utilities/format/format.range/format.range.formatter/format.functions.vformat.pass.cpp FAIL
237221

238222

239223
# *** MISSING COMPILER FEATURES ***
@@ -961,6 +945,7 @@ std/input.output/string.streams/stringstream/stringstream.members/str.allocator_
961945
# These formatter tests need to create basic_format_contexts, so test_format_context.h
962946
# says "Please create a vendor specific version of the test functions".
963947
# If we do, some of these tests should be able to work.
948+
std/containers/sequences/vector.bool/vector.bool.fmt/format.pass.cpp FAIL
964949
std/thread/thread.threads/thread.thread.class/thread.thread.id/format.pass.cpp FAIL
965950
std/utilities/format/format.formatter/format.context/format.context/advance_to.pass.cpp FAIL
966951
std/utilities/format/format.formatter/format.context/format.context/arg.pass.cpp FAIL
@@ -982,6 +967,12 @@ std/utilities/format/format.formatter/format.formatter.spec/formatter.unsigned_i
982967
std/utilities/format/format.range/format.range.formatter/format.pass.cpp FAIL
983968
std/utilities/format/format.range/format.range.formatter/set_brackets.pass.cpp FAIL
984969
std/utilities/format/format.range/format.range.formatter/set_separator.pass.cpp FAIL
970+
std/utilities/format/format.range/format.range.fmtdef/format.pass.cpp FAIL
971+
std/utilities/format/format.range/format.range.fmtdef/set_brackets.pass.cpp FAIL
972+
std/utilities/format/format.range/format.range.fmtdef/set_separator.pass.cpp FAIL
973+
std/utilities/format/format.range/format.range.fmtmap/format.pass.cpp FAIL
974+
std/utilities/format/format.range/format.range.fmtset/format.pass.cpp FAIL
975+
std/utilities/format/format.range/format.range.fmtstr/format.pass.cpp FAIL
985976
std/utilities/format/format.tuple/format.pass.cpp FAIL
986977

987978
# Not analyzed. Apparent false positives from static analysis where it thinks that array indexing is out of bounds.

tests/std/test.lst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,7 +619,12 @@ tests\P2286R8_text_formatting_escaping_legacy_text_encoding
619619
tests\P2286R8_text_formatting_escaping_utf8
620620
tests\P2286R8_text_formatting_formattable
621621
tests\P2286R8_text_formatting_range_formatter
622+
tests\P2286R8_text_formatting_range_map
623+
tests\P2286R8_text_formatting_range_sequence
624+
tests\P2286R8_text_formatting_range_set
625+
tests\P2286R8_text_formatting_range_string
622626
tests\P2286R8_text_formatting_tuple
627+
tests\P2286R8_text_formatting_tuple_disambiguation
623628
tests\P2286R8_text_formatting_vector_bool_reference
624629
tests\P2302R4_ranges_alg_contains
625630
tests\P2302R4_ranges_alg_contains_subrange

tests/std/tests/P2286R8_text_formatting_formattable/test.compile.pass.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,26 @@ void test_P2286_vector_bool() {
213213
// Tests for P2286 Formatting ranges
214214
template <class CharT>
215215
void test_P2286() {
216+
assert_is_formattable<array<int, 42>, CharT>();
217+
assert_is_formattable<vector<int>, CharT>();
218+
assert_is_formattable<deque<int>, CharT>();
219+
assert_is_formattable<forward_list<int>, CharT>();
220+
assert_is_formattable<list<int>, CharT>();
221+
222+
assert_is_formattable<set<int>, CharT>();
223+
assert_is_formattable<map<int, int>, CharT>();
224+
assert_is_formattable<multiset<int>, CharT>();
225+
assert_is_formattable<multimap<int, int>, CharT>();
226+
227+
assert_is_formattable<unordered_set<int>, CharT>();
228+
assert_is_formattable<unordered_map<int, int>, CharT>();
229+
assert_is_formattable<unordered_multiset<int>, CharT>();
230+
assert_is_formattable<unordered_multimap<int, int>, CharT>();
231+
232+
assert_is_formattable<span<int>, CharT>();
233+
234+
assert_is_formattable<valarray<int>, CharT>();
235+
216236
assert_is_formattable<pair<int, int>, CharT>();
217237
assert_is_formattable<tuple<int>, CharT>();
218238

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3+
4+
RUNALL_INCLUDE ..\usual_latest_matrix.lst

0 commit comments

Comments
 (0)