diff --git a/libcxx/include/__format/range_formatter.h b/libcxx/include/__format/range_formatter.h --- a/libcxx/include/__format/range_formatter.h +++ b/libcxx/include/__format/range_formatter.h @@ -165,11 +165,16 @@ // When the range is contiguous use a basic_string_view instead to avoid a // copy of the underlying data. The basic_string_view formatter // specialization is the "basic" string formatter in libc++. - if constexpr (ranges::contiguous_range<_Rp>) { + if constexpr (ranges::contiguous_range<_Rp> && ranges::sized_range<_Rp>) { std::formatter, _CharT> __formatter; if (__debug_format) __formatter.set_debug_format(); - return __formatter.format(basic_string_view<_CharT>{__range.data(), __range.size()}, __ctx); + return __formatter.format( + basic_string_view<_CharT>{ + ranges::data(__range), + ranges::size(__range), + }, + __ctx); } else { std::formatter, _CharT> __formatter; if (__debug_format) diff --git a/libcxx/test/std/utilities/format/format.range/format.range.formatter/format.functions.tests.h b/libcxx/test/std/utilities/format/format.range/format.range.formatter/format.functions.tests.h --- a/libcxx/test/std/utilities/format/format.range/format.range.formatter/format.functions.tests.h +++ b/libcxx/test/std/utilities/format/format.range/format.range.formatter/format.functions.tests.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -1258,6 +1259,66 @@ check, check_exception, std::counted_iterator{contiguous_iterator(input.data()), input.size()}); } +// +// Adaptor +// + +template +class non_contiguous { + // A deque iterator is random access, but not contiguous. + using adaptee = std::deque; + +public: + using iterator = typename adaptee::iterator; + using pointer = typename adaptee::pointer; + + iterator begin() { return data_.begin(); } + iterator end() { return data_.end(); } + + explicit non_contiguous(adaptee&& data) : data_(std::move(data)) {} + +private: + adaptee data_; +}; + +template +class contiguous { + // A vector iterator is contiguous. + using adaptee = std::vector; + +public: + using iterator = typename adaptee::iterator; + using pointer = typename adaptee::pointer; + + iterator begin() { return data_.begin(); } + iterator end() { return data_.end(); } + + explicit contiguous(adaptee&& data) : data_(std::move(data)) {} + +private: + adaptee data_; +}; + +// This tests two different implementations in libc++. A basic_string_view +// formatter if the range is contiguous, a basic_string otherwise. +template +void test_adaptor(TestFunction check, ExceptionTest check_exception) { + static_assert(std::format_kind> == std::range_format::sequence); + static_assert(std::ranges::sized_range>); + static_assert(!std::ranges::contiguous_range>); + test_char_string( + check, + check_exception, + non_contiguous{std::deque{CharT('H'), CharT('e'), CharT('l'), CharT('l'), CharT('o')}}); + + static_assert(std::format_kind> == std::range_format::sequence); + static_assert(std::ranges::sized_range>); + static_assert(std::ranges::contiguous_range>); + test_char_string(check, + check_exception, + contiguous{std::vector{CharT('H'), CharT('e'), CharT('l'), CharT('l'), CharT('o')}}); +} + // // Driver // @@ -1282,6 +1343,8 @@ test_tuple_int_int_int(check, check_exception); test_with_ranges(check, check_exception); + + test_adaptor(check, check_exception); } #endif // TEST_STD_UTILITIES_FORMAT_FORMAT_RANGE_FORMAT_RANGE_FORMATTER_FORMAT_FUNCTIONS_TESTS_H