[libc++][format] Implements range_formatter

Implements parts of
- P2286R8 Formatting Ranges
- P2585R0 Improving default container formatting

Depends on D140651

Reviewed By: ldionne, #libc

Differential Revision: https://reviews.llvm.org/D140653
This commit is contained in:
Mark de Wever 2022-05-05 18:57:32 +02:00
parent 72dc033fa6
commit 22e8525dfd
30 changed files with 2280 additions and 11 deletions

View File

@ -30,7 +30,7 @@ Section,Description,Dependencies,Assignee,Status,First released version
`P2286R8 <https://wg21.link/P2286R8>`__,"Formatting ranges"
`[format.syn] <https://wg21.link/format.syn>`_,"Concept ``formattable``",,Mark de Wever,|Complete|, Clang 16
`[format.string.std] <https://wg21.link/format.string.std>`_,"std-format-spec ``type`` debug",,Mark de Wever,|Complete|,Clang 16
`[format.range] <https://wg21.link/format.range>`_,"Formatting for ranges: sequences",,Mark de Wever,|In Progress|,
`[format.range] <https://wg21.link/format.range>`_,"Formatting for ranges: sequences",,Mark de Wever,|Complete|,Clang 16
`[format.range] <https://wg21.link/format.range>`_,"Formatting for ranges: associative",,Mark de Wever,,
`[format.range] <https://wg21.link/format.range>`_,"Formatting for ranges: container adaptors",,Mark de Wever,,
`[format.range] <https://wg21.link/format.range>`_,"Formatting for ranges: ``pair`` and ``tuple``",,Mark de Wever,|Complete|,Clang 16

Can't render this file because it has a wrong number of fields in line 2.

View File

@ -327,6 +327,7 @@ set(files
__format/formatter_tuple.h
__format/parser_std_format_spec.h
__format/range_default_formatter.h
__format/range_formatter.h
__format/unicode.h
__functional/binary_function.h
__functional/binary_negate.h

View File

@ -31,6 +31,7 @@
#include <cstddef>
#include <string_view>
#include <type_traits>
#include <vector>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@ -493,6 +494,74 @@ public:
return {_VSTD::move(this->__writer_).__out_it(), this->__size_};
}
};
// A dynamically growing buffer intended to be used for retargeting a context.
//
// P2286 Formatting ranges adds range formatting support. It allows the user to
// specify the minimum width for the entire formatted range. The width of the
// range is not known until the range is formatted. Formatting is done to an
// output_iterator so there's no guarantee it would be possible to add the fill
// to the front of the output. Instead the range is formatted to a temporary
// buffer and that buffer is formatted as a string.
//
// There is an issue with that approach, the format context used in
// std::formatter<T>::format contains the output iterator used as part of its
// type. So using this output iterator means there needs to be a new format
// context and the format arguments need to be retargeted to the new context.
// This retargeting is done by a basic_format_context specialized for the
// __iterator of this container.
template <__fmt_char_type _CharT>
class _LIBCPP_TEMPLATE_VIS __retarget_buffer {
public:
using value_type = _CharT;
struct __iterator {
using difference_type = ptrdiff_t;
_LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(__retarget_buffer& __buffer)
: __buffer_(std::addressof(__buffer)) {}
_LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator=(const _CharT& __c) {
__buffer_->push_back(__c);
return *this;
}
_LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator=(_CharT&& __c) {
__buffer_->push_back(__c);
return *this;
}
_LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator*() { return *this; }
_LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() { return *this; }
_LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int) { return *this; }
__retarget_buffer* __buffer_;
};
_LIBCPP_HIDE_FROM_ABI explicit __retarget_buffer(size_t __size_hint) { __buffer_.reserve(__size_hint); }
_LIBCPP_HIDE_FROM_ABI __iterator __make_output_iterator() { return __iterator{*this}; }
_LIBCPP_HIDE_FROM_ABI void push_back(_CharT __c) { __buffer_.push_back(__c); }
template <__fmt_char_type _InCharT>
_LIBCPP_HIDE_FROM_ABI void __copy(basic_string_view<_InCharT> __str) {
__buffer_.insert(__buffer_.end(), __str.begin(), __str.end());
}
template <__fmt_char_type _InCharT, class _UnaryOperation>
_LIBCPP_HIDE_FROM_ABI void __transform(const _InCharT* __first, const _InCharT* __last, _UnaryOperation __operation) {
_LIBCPP_ASSERT(__first <= __last, "not a valid range");
std::transform(__first, __last, std::back_inserter(__buffer_), std::move(__operation));
}
_LIBCPP_HIDE_FROM_ABI void __fill(size_t __n, _CharT __value) { __buffer_.insert(__buffer_.end(), __n, __value); }
_LIBCPP_HIDE_FROM_ABI basic_string_view<_CharT> __view() { return {__buffer_.data(), __buffer_.size()}; }
private:
// Use vector instead of string to avoid adding zeros after every append
// operation. The buffer is exposed as a string_view and not as a c-string.
vector<_CharT> __buffer_;
};
} // namespace __format
#endif //_LIBCPP_STD_VER > 17

View File

@ -11,13 +11,19 @@
#define _LIBCPP___FORMAT_FORMAT_CONTEXT_H
#include <__availability>
#include <__concepts/same_as.h>
#include <__config>
#include <__format/buffer.h>
#include <__format/format_arg.h>
#include <__format/format_arg_store.h>
#include <__format/format_args.h>
#include <__format/format_error.h>
#include <__format/format_fwd.h>
#include <__iterator/back_insert_iterator.h>
#include <__iterator/concepts.h>
#include <__memory/addressof.h>
#include <__utility/move.h>
#include <__variant/monostate.h>
#include <cstddef>
#ifndef _LIBCPP_HAS_NO_LOCALIZATION
@ -138,8 +144,78 @@ private:
: __out_it_(_VSTD::move(__out_it)), __args_(__args) {}
#endif
};
_LIBCPP_CTAD_SUPPORTED_FOR_TYPE(basic_format_context);
// A specialization for __retarget_buffer
//
// See __retarget_buffer for the motivation for this specialization.
//
// This context holds a reference to the instance of the basic_format_context
// that is retargeted. It converts a formatting argument when it is requested
// during formatting. It is expected that the usage of the arguments is rare so
// the lookups are not expected to be used often. An alternative would be to
// convert all elements during construction.
//
// The elements of the retargets context are only used when an underlying
// formatter uses a locale specific formatting or an formatting argument is
// part for the format spec. For example
// format("{:256:{}}", input, 8);
// Here the width of an element in input is determined dynamically.
// Note when the top-level element has no width the retargeting is not needed.
template <class _CharT>
class _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT
basic_format_context<typename __format::__retarget_buffer<_CharT>::__iterator, _CharT> {
public:
using iterator = typename __format::__retarget_buffer<_CharT>::__iterator;
using char_type = _CharT;
template <class _Tp>
using formatter_type = formatter<_Tp, _CharT>;
template <class _Context>
_LIBCPP_HIDE_FROM_ABI explicit basic_format_context(iterator __out_it, _Context& __ctx)
: __out_it_(std::move(__out_it)),
# ifndef _LIBCPP_HAS_NO_LOCALIZATION
__loc_([](void* __c) { return static_cast<_Context*>(__c)->locale(); }),
# endif
__ctx_(std::addressof(__ctx)),
__arg_([](void* __c, size_t __id) {
return std::visit_format_arg(
[&](auto __arg) -> basic_format_arg<basic_format_context> {
if constexpr (same_as<decltype(__arg), monostate>)
return {};
else if constexpr (same_as<decltype(__arg), typename basic_format_arg<_Context>::handle>)
// At the moment it's not possible for formatting to use a re-targeted handle.
// TODO FMT add this when support is needed.
std::__throw_format_error("Re-targeting handle not supported");
else
return basic_format_arg<basic_format_context>{
__format::__determine_arg_t<basic_format_context, decltype(__arg)>(),
__basic_format_arg_value<basic_format_context>(__arg)};
},
static_cast<_Context*>(__c)->arg(__id));
}) {
}
_LIBCPP_HIDE_FROM_ABI basic_format_arg<basic_format_context> arg(size_t __id) const noexcept {
return __arg_(__ctx_, __id);
}
# ifndef _LIBCPP_HAS_NO_LOCALIZATION
_LIBCPP_HIDE_FROM_ABI _VSTD::locale locale() { return __loc_(__ctx_); }
# endif
_LIBCPP_HIDE_FROM_ABI iterator out() { return std::move(__out_it_); }
_LIBCPP_HIDE_FROM_ABI void advance_to(iterator __it) { __out_it_ = std::move(__it); }
private:
iterator __out_it_;
# ifndef _LIBCPP_HAS_NO_LOCALIZATION
std::locale (*__loc_)(void* __ctx);
# endif
void* __ctx_;
basic_format_arg<basic_format_context> (*__arg_)(void* __ctx, size_t __id);
};
_LIBCPP_CTAD_SUPPORTED_FOR_TYPE(basic_format_context);
#endif //_LIBCPP_STD_VER > 17
_LIBCPP_END_NAMESPACE_STD

View File

@ -102,6 +102,10 @@ _LIBCPP_HIDE_FROM_ABI auto __copy(basic_string_view<_CharT> __str, output_iterat
if constexpr (_VSTD::same_as<decltype(__out_it), _VSTD::back_insert_iterator<__format::__output_buffer<_OutCharT>>>) {
__out_it.__get_container()->__copy(__str);
return __out_it;
} else if constexpr (_VSTD::same_as<decltype(__out_it),
typename __format::__retarget_buffer<_OutCharT>::__iterator>) {
__out_it.__buffer_->__copy(__str);
return __out_it;
} else {
return std::ranges::copy(__str, _VSTD::move(__out_it)).out;
}
@ -132,6 +136,10 @@ __transform(const _CharT* __first,
if constexpr (_VSTD::same_as<decltype(__out_it), _VSTD::back_insert_iterator<__format::__output_buffer<_OutCharT>>>) {
__out_it.__get_container()->__transform(__first, __last, _VSTD::move(__operation));
return __out_it;
} else if constexpr (_VSTD::same_as<decltype(__out_it),
typename __format::__retarget_buffer<_OutCharT>::__iterator>) {
__out_it.__buffer_->__transform(__first, __last, _VSTD::move(__operation));
return __out_it;
} else {
return std::ranges::transform(__first, __last, _VSTD::move(__out_it), __operation).out;
}
@ -145,6 +153,9 @@ _LIBCPP_HIDE_FROM_ABI _OutIt __fill(_OutIt __out_it, size_t __n, _CharT __value)
if constexpr (_VSTD::same_as<decltype(__out_it), _VSTD::back_insert_iterator<__format::__output_buffer<_CharT>>>) {
__out_it.__get_container()->__fill(__n, __value);
return __out_it;
} else if constexpr (_VSTD::same_as<decltype(__out_it), typename __format::__retarget_buffer<_CharT>::__iterator>) {
__out_it.__buffer_->__fill(__n, __value);
return __out_it;
} else {
return std::ranges::fill_n(_VSTD::move(__out_it), __n, __value);
}

View File

@ -139,6 +139,7 @@ inline constexpr __fields __fields_pointer{.__type_ = true};
# if _LIBCPP_STD_VER > 20
inline constexpr __fields __fields_tuple{.__type_ = false, .__allow_colon_in_fill_ = true};
inline constexpr __fields __fields_range{.__type_ = false, .__allow_colon_in_fill_ = true};
# endif
enum class _LIBCPP_ENUM_VIS __alignment : uint8_t {

View File

@ -19,9 +19,11 @@
#include <__config>
#include <__format/concepts.h>
#include <__format/formatter.h>
#include <__format/range_formatter.h>
#include <__ranges/concepts.h>
#include <__type_traits/remove_cvref.h>
#include <__utility/pair.h>
#include <string_view>
#include <tuple>
_LIBCPP_BEGIN_NAMESPACE_STD
@ -104,7 +106,28 @@ struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT __range_default_formatte
template <ranges::input_range _Rp, class _CharT>
struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT __range_default_formatter<range_format::sequence, _Rp, _CharT> {
__range_default_formatter() = delete; // TODO FMT Implement
private:
using __maybe_const_r = __fmt_maybe_const<_Rp, _CharT>;
range_formatter<remove_cvref_t<ranges::range_reference_t<__maybe_const_r>>, _CharT> __underlying_;
public:
_LIBCPP_HIDE_FROM_ABI constexpr void set_separator(basic_string_view<_CharT> __separator) {
__underlying_.set_separator(__separator);
}
_LIBCPP_HIDE_FROM_ABI constexpr void
set_brackets(basic_string_view<_CharT> __opening_bracket, basic_string_view<_CharT> __closing_bracket) {
__underlying_.set_brackets(__opening_bracket, __closing_bracket);
}
template <class _ParseContext>
_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
return __underlying_.parse(__ctx);
}
template <class FormatContext>
_LIBCPP_HIDE_FROM_ABI typename FormatContext::iterator format(__maybe_const_r& __range, FormatContext& __ctx) const {
return __underlying_.format(__range, __ctx);
}
};
template <ranges::input_range _Rp, class _CharT>

View File

@ -0,0 +1,245 @@
// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef _LIBCPP___FORMAT_RANGE_FORMATTER_H
#define _LIBCPP___FORMAT_RANGE_FORMATTER_H
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif
#include <__algorithm/ranges_copy.h>
#include <__availability>
#include <__chrono/statically_widen.h>
#include <__concepts/same_as.h>
#include <__config>
#include <__format/buffer.h>
#include <__format/concepts.h>
#include <__format/format_args.h>
#include <__format/format_context.h>
#include <__format/format_error.h>
#include <__format/formatter.h>
#include <__format/formatter_output.h>
#include <__format/parser_std_format_spec.h>
#include <__iterator/back_insert_iterator.h>
#include <__ranges/concepts.h>
#include <__type_traits/remove_cvref.h>
#include <string_view>
_LIBCPP_BEGIN_NAMESPACE_STD
#if _LIBCPP_STD_VER > 20
template <class _Tp, class _CharT = char>
requires same_as<remove_cvref_t<_Tp>, _Tp> && formattable<_Tp, _CharT>
struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT range_formatter {
_LIBCPP_HIDE_FROM_ABI constexpr void set_separator(basic_string_view<_CharT> __separator) {
__separator_ = __separator;
}
_LIBCPP_HIDE_FROM_ABI constexpr void
set_brackets(basic_string_view<_CharT> __opening_bracket, basic_string_view<_CharT> __closing_bracket) {
__opening_bracket_ = __opening_bracket;
__closing_bracket_ = __closing_bracket;
}
_LIBCPP_HIDE_FROM_ABI constexpr formatter<_Tp, _CharT>& underlying() { return __underlying_; }
_LIBCPP_HIDE_FROM_ABI constexpr const formatter<_Tp, _CharT>& underlying() const { return __underlying_; }
template <class _ParseContext>
_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __parse_ctx) {
const _CharT* __begin = __parser_.__parse(__parse_ctx, __format_spec::__fields_range);
const _CharT* __end = __parse_ctx.end();
if (__begin == __end)
return __begin;
// The n field overrides a possible m type, therefore delay applying the
// effect of n until the type has been procesed.
bool __clear_brackets = (*__begin == _CharT('n'));
if (__clear_brackets) {
++__begin;
if (__begin == __end) {
// Since there is no more data, clear the brackets before returning.
set_brackets({}, {});
return __begin;
}
}
__parse_type(__begin, __end);
if (__clear_brackets)
set_brackets({}, {});
if (__begin == __end)
return __begin;
bool __has_range_underlying_spec = *__begin == _CharT(':');
if (__parser_.__type_ != __format_spec::__type::__default) {
// [format.range.formatter]/6
// If the range-type is s or ?s, then there shall be no n option and no
// range-underlying-spec.
if (__clear_brackets) {
if (__parser_.__type_ == __format_spec::__type::__string)
std::__throw_format_error("The n option and type s can't be used together");
std::__throw_format_error("The n option and type ?s can't be used together");
}
if (__has_range_underlying_spec) {
if (__parser_.__type_ == __format_spec::__type::__string)
std::__throw_format_error("Type s and an underlying format specification can't be used together");
std::__throw_format_error("Type ?s and an underlying format specification can't be used together");
}
} else if (!__has_range_underlying_spec)
std::__set_debug_format(__underlying_);
if (__has_range_underlying_spec) {
// range-underlying-spec:
// : format-spec
++__begin;
if (__begin == __end)
return __begin;
__parse_ctx.advance_to(__begin);
__begin = __underlying_.parse(__parse_ctx);
}
if (__begin != __end && *__begin != _CharT('}'))
std::__throw_format_error("The format-spec should consume the input or end with a '}'");
return __begin;
}
template <ranges::input_range _Rp, class _FormatContext>
requires formattable<ranges::range_reference_t<_Rp>, _CharT> &&
same_as<remove_cvref_t<ranges::range_reference_t<_Rp>>, _Tp>
_LIBCPP_HIDE_FROM_ABI typename _FormatContext::iterator format(_Rp&& __range, _FormatContext& __ctx) const {
__format_spec::__parsed_specifications<_CharT> __specs = __parser_.__get_parsed_std_specifications(__ctx);
if (!__specs.__has_width())
return __format_range(__range, __ctx, __specs);
// The size of the buffer needed is:
// - open bracket characters
// - close bracket character
// - n elements where every element may have a different size
// - (n -1) separators
// The size of the element is hard to predict, knowing the type helps but
// it depends on the format-spec. As an initial estimate we guess 6
// characters.
// Typically both brackets are 1 character and the separator is 2
// characters. Which means there will be
// (n - 1) * 2 + 1 + 1 = n * 2 character
// So estimate 8 times the range size as buffer.
__format::__retarget_buffer<_CharT> __buffer{8 * ranges::size(__range)};
basic_format_context<typename __format::__retarget_buffer<_CharT>::__iterator, _CharT> __c{
__buffer.__make_output_iterator(), __ctx};
__format_range(__range, __c, __specs);
return __formatter::__write_string_no_precision(__buffer.__view(), __ctx.out(), __specs);
}
template <ranges::input_range _Rp, class _FormatContext>
typename _FormatContext::iterator _LIBCPP_HIDE_FROM_ABI
__format_range(_Rp&& __range, _FormatContext& __ctx, __format_spec::__parsed_specifications<_CharT> __specs) const {
if constexpr (same_as<_Tp, _CharT>) {
switch (__specs.__std_.__type_) {
case __format_spec::__type::__string:
case __format_spec::__type::__debug:
return __format_as_string(__range, __ctx, __specs.__std_.__type_ == __format_spec::__type::__debug);
default:
return __format_as_sequence(__range, __ctx);
}
} else
return __format_as_sequence(__range, __ctx);
}
template <ranges::input_range _Rp, class _FormatContext>
_LIBCPP_HIDE_FROM_ABI typename _FormatContext::iterator
__format_as_string(_Rp&& __range, _FormatContext& __ctx, bool __debug_format) const {
// 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>) {
std::formatter<basic_string_view<_CharT>, _CharT> __formatter;
if (__debug_format)
__formatter.set_debug_format();
return __formatter.format(basic_string_view<_CharT>{__range.data(), __range.size()}, __ctx);
} else {
std::formatter<basic_string<_CharT>, _CharT> __formatter;
if (__debug_format)
__formatter.set_debug_format();
// P2106's from_range has not been implemented yet. Instead use a simple
// copy operation.
// TODO FMT use basic_string's "from_range" constructor.
// return std::formatter<basic_string<_CharT>, _CharT>{}.format(basic_string<_CharT>{from_range, __range}, __ctx);
basic_string<_CharT> __str;
ranges::copy(__range, back_insert_iterator{__str});
return __formatter.format(__str, __ctx);
}
}
template <ranges::input_range _Rp, class _FormatContext>
_LIBCPP_HIDE_FROM_ABI typename _FormatContext::iterator
__format_as_sequence(_Rp&& __range, _FormatContext& __ctx) const {
__ctx.advance_to(ranges::copy(__opening_bracket_, __ctx.out()).out);
bool __use_separator = false;
for (auto&& __e : __range) {
if (__use_separator)
__ctx.advance_to(ranges::copy(__separator_, __ctx.out()).out);
else
__use_separator = true;
__ctx.advance_to(__underlying_.format(__e, __ctx));
}
return ranges::copy(__closing_bracket_, __ctx.out()).out;
}
__format_spec::__parser<_CharT> __parser_{.__alignment_ = __format_spec::__alignment::__left};
private:
_LIBCPP_HIDE_FROM_ABI constexpr void __parse_type(const _CharT*& __begin, const _CharT* __end) {
switch (*__begin) {
case _CharT('m'):
if constexpr (__fmt_pair_like<_Tp>) {
set_brackets(_LIBCPP_STATICALLY_WIDEN(_CharT, "{"), _LIBCPP_STATICALLY_WIDEN(_CharT, "}"));
set_separator(_LIBCPP_STATICALLY_WIDEN(_CharT, ", "));
++__begin;
} else
std::__throw_format_error("The range-format-spec type m requires two elements for a pair or tuple");
break;
case _CharT('s'):
if constexpr (same_as<_Tp, _CharT>) {
__parser_.__type_ = __format_spec::__type::__string;
++__begin;
} else
std::__throw_format_error("The range-format-spec type s requires formatting a character type");
break;
case _CharT('?'):
++__begin;
if (__begin == __end || *__begin != _CharT('s'))
std::__throw_format_error("The format-spec should consume the input or end with a '}'");
if constexpr (same_as<_Tp, _CharT>) {
__parser_.__type_ = __format_spec::__type::__debug;
++__begin;
} else
std::__throw_format_error("The range-format-spec type ?s requires formatting a character type");
}
}
formatter<_Tp, _CharT> __underlying_;
basic_string_view<_CharT> __separator_ = _LIBCPP_STATICALLY_WIDEN(_CharT, ", ");
basic_string_view<_CharT> __opening_bracket_ = _LIBCPP_STATICALLY_WIDEN(_CharT, "[");
basic_string_view<_CharT> __closing_bracket_ = _LIBCPP_STATICALLY_WIDEN(_CharT, "]");
};
#endif //_LIBCPP_STD_VER > 20
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP___FORMAT_RANGE_FORMATTER_H

View File

@ -25,6 +25,7 @@ struct allocation_result {
_Pointer ptr;
size_t count;
};
_LIBCPP_CTAD_SUPPORTED_FOR_TYPE(allocation_result);
template <class _Alloc>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr

View File

@ -130,6 +130,11 @@ namespace std {
requires same_as<R, remove_cvref_t<R>>
constexpr range_format format_kind<R> = see below; // since C++23
// [format.range.formatter], class template range_formatter
template<class T, class charT = char>
requires same_as<remove_cvref_t<T>, T> && formattable<T, charT>
class range_formatter; // since C++23
// [format.range.fmtdef], class template range-default-formatter
template<range_format K, ranges::input_range R, class charT>
struct range-default-formatter; // exposition only, since C++23
@ -194,6 +199,7 @@ namespace std {
#include <__format/formatter_tuple.h>
#include <__format/parser_std_format_spec.h>
#include <__format/range_default_formatter.h>
#include <__format/range_formatter.h>
#include <__format/unicode.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)

View File

@ -871,6 +871,7 @@ module std [system] {
module formatter_tuple { private header "__format/formatter_tuple.h" }
module parser_std_format_spec { private header "__format/parser_std_format_spec.h" }
module range_default_formatter { private header "__format/range_default_formatter.h" }
module range_formatter { private header "__format/range_formatter.h" }
module unicode { private header "__format/unicode.h" }
}
}

View File

@ -359,6 +359,7 @@ END-SCRIPT
#include <__format/formatter_tuple.h> // expected-error@*:* {{use of private header from outside its module: '__format/formatter_tuple.h'}}
#include <__format/parser_std_format_spec.h> // expected-error@*:* {{use of private header from outside its module: '__format/parser_std_format_spec.h'}}
#include <__format/range_default_formatter.h> // expected-error@*:* {{use of private header from outside its module: '__format/range_default_formatter.h'}}
#include <__format/range_formatter.h> // expected-error@*:* {{use of private header from outside its module: '__format/range_formatter.h'}}
#include <__format/unicode.h> // expected-error@*:* {{use of private header from outside its module: '__format/unicode.h'}}
#include <__functional/binary_function.h> // expected-error@*:* {{use of private header from outside its module: '__functional/binary_function.h'}}
#include <__functional/binary_negate.h> // expected-error@*:* {{use of private header from outside its module: '__functional/binary_negate.h'}}

View File

@ -359,6 +359,7 @@ format string
format string_view
format tuple
format type_traits
format vector
format version
forward_list algorithm
forward_list atomic

1 algorithm atomic
359 format string_view
360 format tuple
361 format type_traits
362 format vector
363 format version
364 forward_list algorithm
365 forward_list atomic

View File

@ -359,6 +359,7 @@ format string
format string_view
format tuple
format type_traits
format vector
format version
forward_list algorithm
forward_list atomic

1 algorithm atomic
359 format string_view
360 format tuple
361 format type_traits
362 format vector
363 format version
364 forward_list algorithm
365 forward_list atomic

View File

@ -361,6 +361,7 @@ format string
format string_view
format tuple
format type_traits
format vector
format version
forward_list algorithm
forward_list atomic

1 algorithm atomic
361 format string_view
362 format tuple
363 format type_traits
364 format vector
365 format version
366 forward_list algorithm
367 forward_list atomic

View File

@ -361,6 +361,7 @@ format string
format string_view
format tuple
format type_traits
format vector
format version
forward_list algorithm
forward_list atomic

1 algorithm atomic
361 format string_view
362 format tuple
363 format type_traits
364 format vector
365 format version
366 forward_list algorithm
367 forward_list atomic

View File

@ -125,6 +125,7 @@ chrono string
chrono string_view
chrono tuple
chrono type_traits
chrono vector
chrono version
cinttypes cstdint
cmath type_traits
@ -369,6 +370,7 @@ format string
format string_view
format tuple
format type_traits
format vector
format version
forward_list algorithm
forward_list atomic

1 algorithm atomic
125 chrono string_view
126 chrono tuple
127 chrono type_traits
128 chrono vector
129 chrono version
130 cinttypes cstdint
131 cmath type_traits
370 format string_view
371 format tuple
372 format type_traits
373 format vector
374 format version
375 forward_list algorithm
376 forward_list atomic

View File

@ -95,6 +95,7 @@ chrono string
chrono string_view
chrono tuple
chrono type_traits
chrono vector
chrono version
cinttypes cstdint
cmath version
@ -277,6 +278,7 @@ format string
format string_view
format tuple
format type_traits
format vector
format version
forward_list compare
forward_list cstddef

1 algorithm bit
95 chrono string_view
96 chrono tuple
97 chrono type_traits
98 chrono vector
99 chrono version
100 cinttypes cstdint
101 cmath version
278 format string_view
279 format tuple
280 format type_traits
281 format vector
282 format version
283 forward_list compare
284 forward_list cstddef

View File

@ -200,11 +200,11 @@ void test_P1636() {
// TODO validate whether the test is correct after the paper has been accepted.
template <class CharT>
void test_P2286() {
assert_is_not_formattable<std::array<int, 42>, CharT>();
assert_is_not_formattable<std::vector<int>, CharT>();
assert_is_not_formattable<std::deque<int>, CharT>();
assert_is_not_formattable<std::forward_list<int>, CharT>();
assert_is_not_formattable<std::list<int>, CharT>();
assert_is_formattable<std::array<int, 42>, CharT>();
assert_is_formattable<std::vector<int>, CharT>();
assert_is_formattable<std::deque<int>, CharT>();
assert_is_formattable<std::forward_list<int>, CharT>();
assert_is_formattable<std::list<int>, CharT>();
assert_is_not_formattable<std::set<int>, CharT>();
assert_is_not_formattable<std::map<int, int>, CharT>();
@ -220,9 +220,9 @@ void test_P2286() {
assert_is_not_formattable<std::queue<int>, CharT>();
assert_is_not_formattable<std::priority_queue<int>, CharT>();
assert_is_not_formattable<std::span<int>, CharT>();
assert_is_formattable<std::span<int>, CharT>();
assert_is_not_formattable<std::valarray<int>, CharT>();
assert_is_formattable<std::valarray<int>, CharT>();
assert_is_formattable<std::pair<int, int>, CharT>();
assert_is_formattable<std::tuple<int>, CharT>();

View File

@ -0,0 +1,58 @@
//===----------------------------------------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-has-no-incomplete-format
// TODO FMT Fix this test using GCC, it currently times out.
// UNSUPPORTED: gcc-12
// This test requires the dylib support introduced in D92214.
// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{.+}}
// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx11.{{.+}}
// <format>
// template<class T, class charT = char>
// requires same_as<remove_cvref_t<T>, T> && formattable<T, charT>
// class range_formatter;
// template<class... Args>
// string format(format_string<Args...> fmt, Args&&... args);
// template<class... Args>
// wstring format(wformat_string<Args...> fmt, Args&&... args);
#include <format>
#include <cassert>
#include "format.functions.tests.h"
#include "test_format_string.h"
#include "test_macros.h"
#include "assert_macros.h"
auto test = []<class CharT, class... Args>(
std::basic_string_view<CharT> expected, test_format_string<CharT, Args...> fmt, Args&&... args) {
std::basic_string<CharT> out = std::format(fmt, std::forward<Args>(args)...);
TEST_REQUIRE(
out == expected,
test_concat_message("\nFormat string ", fmt, "\nExpected output ", expected, "\nActual output ", out, '\n'));
};
auto test_exception = []<class CharT, class... Args>(std::string_view, std::basic_string_view<CharT>, Args&&...) {
// After P2216 most exceptions thrown by std::format become ill-formed.
// Therefore this tests does nothing.
};
int main(int, char**) {
format_tests<char>(test, test_exception);
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
format_tests<wchar_t>(test, test_exception);
#endif
return 0;
}

View File

@ -0,0 +1,71 @@
//===----------------------------------------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-has-no-incomplete-format
// TODO FMT Fix this test using GCC, it currently times out.
// UNSUPPORTED: gcc-12
// This test requires the dylib support introduced in D92214.
// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{.+}}
// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx11.{{.+}}
// <format>
// template<class T, class charT = char>
// requires same_as<remove_cvref_t<T>, T> && formattable<T, charT>
// class range_formatter;
// string vformat(string_view fmt, format_args args);
// wstring vformat(wstring_view fmt, wformat_args args);
#include <format>
#include <cassert>
#include "format.functions.tests.h"
#include "test_macros.h"
#include "assert_macros.h"
auto test = []<class CharT, class... Args>(
std::basic_string_view<CharT> expected, std::basic_string_view<CharT> fmt, Args&&... args) {
std::basic_string<CharT> out = std::vformat(fmt, std::make_format_args<context_t<CharT>>(args...));
TEST_REQUIRE(
out == expected,
test_concat_message("\nFormat string ", fmt, "\nExpected output ", expected, "\nActual output ", out, '\n'));
};
auto test_exception =
[]<class CharT, class... Args>(
[[maybe_unused]] std::string_view what,
[[maybe_unused]] std::basic_string_view<CharT> fmt,
[[maybe_unused]] Args&&... args) {
#ifndef TEST_HAS_NO_EXCEPTIONS
try {
TEST_IGNORE_NODISCARD std::vformat(fmt, std::make_format_args<context_t<CharT>>(args...));
TEST_FAIL(test_concat_message("\nFormat string ", fmt, "\nDidn't throw an exception.\n"));
} catch (const std::format_error& e) {
TEST_LIBCPP_REQUIRE(
e.what() == what,
test_concat_message(
"\nFormat string ", fmt, "\nExpected exception ", what, "\nActual exception ", e.what(), '\n'));
return;
}
assert(false);
#endif
};
int main(int, char**) {
format_tests<char>(test, test_exception);
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
format_tests<wchar_t>(test, test_exception);
#endif
return 0;
}

View File

@ -0,0 +1,75 @@
//===----------------------------------------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-has-no-incomplete-format
// TODO FMT Fix this test using GCC, it currently times out.
// UNSUPPORTED: gcc-12
// This test requires the dylib support introduced in D92214.
// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{.+}}
// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx11.{{.+}}
// <format>
// template<class T, class charT = char>
// requires same_as<remove_cvref_t<T>, T> && formattable<T, charT>
// class range_formatter
// template<class FormatContext>
// typename FormatContext::iterator
// format(const T& ref, FormatContext& ctx) const;
// Note this tests the basics of this function. It's tested in more detail in
// the format functions test.
#include <cassert>
#include <concepts>
#include <format>
#include <vector>
#include "test_format_context.h"
#include "test_macros.h"
#include "make_string.h"
#define SV(S) MAKE_STRING_VIEW(CharT, S)
template <class StringViewT>
void test_format(StringViewT expected, std::vector<int> arg) {
using CharT = typename StringViewT::value_type;
using String = std::basic_string<CharT>;
using OutIt = std::back_insert_iterator<String>;
using FormatCtxT = std::basic_format_context<OutIt, CharT>;
const std::range_formatter<int, CharT> formatter;
String result;
OutIt out = std::back_inserter(result);
FormatCtxT format_ctx = test_format_context_create<OutIt, CharT>(out, std::make_format_args<FormatCtxT>(arg));
formatter.format(arg, format_ctx);
assert(result == expected);
}
template <class CharT>
void test_fmt() {
test_format(SV("[1]"), std::vector<int>{1});
test_format(SV("[0]"), std::vector<int>{0});
}
void test() {
test_fmt<char>();
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
test_fmt<wchar_t>();
#endif
}
int main(int, char**) {
test();
return 0;
}

View File

@ -0,0 +1,75 @@
//===----------------------------------------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-has-no-incomplete-format
// TODO FMT Fix this test using GCC, it currently times out.
// UNSUPPORTED: gcc-12
// This test requires the dylib support introduced in D92214.
// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{.+}}
// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx11.{{.+}}
// <format>
// template<class T, class charT = char>
// requires same_as<remove_cvref_t<T>, T> && formattable<T, charT>
// class range_formatter
// template<class ParseContext>
// constexpr typename ParseContext::iterator
// parse(ParseContext& ctx);
// Note this tests the basics of this function. It's tested in more detail in
// the format functions test.
#include <cassert>
#include <concepts>
#include <format>
#include "test_format_context.h"
#include "test_macros.h"
#include "make_string.h"
#define SV(S) MAKE_STRING_VIEW(CharT, S)
template <class StringViewT>
constexpr void test_parse(StringViewT fmt) {
using CharT = typename StringViewT::value_type;
auto parse_ctx = std::basic_format_parse_context<CharT>(fmt);
std::range_formatter<int, CharT> formatter;
static_assert(std::semiregular<decltype(formatter)>);
std::same_as<typename StringViewT::iterator> auto it = formatter.parse(parse_ctx);
assert(it == fmt.end() - (!fmt.empty() && fmt.back() == '}'));
}
template <class CharT>
constexpr void test_fmt() {
test_parse(SV(""));
test_parse(SV(":d"));
test_parse(SV("}"));
test_parse(SV(":d}"));
}
constexpr bool test() {
test_fmt<char>();
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
test_fmt<wchar_t>();
#endif
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,73 @@
//===----------------------------------------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-has-no-incomplete-format
// TODO FMT Fix this test using GCC, it currently times out.
// UNSUPPORTED: gcc-12
// This test requires the dylib support introduced in D92214.
// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{.+}}
// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx11.{{.+}}
// <format>
// template<class T, class charT = char>
// requires same_as<remove_cvref_t<T>, T> && formattable<T, charT>
// class range_formatter
// constexpr void constexpr void set_brackets(basic_string_view<charT> opening,
// basic_string_view<charT> closing);
// Note this tests the basics of this function. It's tested in more detail in
// the format functions test.
#include <format>
#include <cassert>
#include <type_traits>
#include <vector>
#include "make_string.h"
#include "test_format_context.h"
#define SV(S) MAKE_STRING_VIEW(CharT, S)
template <class CharT>
constexpr void test_setter() {
std::range_formatter<int, CharT> formatter;
formatter.set_brackets(SV("open"), SV("close"));
// Note there is no direct way to validate this function modified the object.
if (!std::is_constant_evaluated()) {
using String = std::basic_string<CharT>;
using OutIt = std::back_insert_iterator<String>;
using FormatCtxT = std::basic_format_context<OutIt, CharT>;
String result;
OutIt out = std::back_inserter(result);
FormatCtxT format_ctx = test_format_context_create<OutIt, CharT>(out, std::make_format_args<FormatCtxT>());
formatter.format(std::vector<int>{0, 42, 99}, format_ctx);
assert(result == SV("open0, 42, 99close"));
}
}
constexpr bool test() {
test_setter<char>();
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
test_setter<wchar_t>();
#endif
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,72 @@
//===----------------------------------------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-has-no-incomplete-format
// TODO FMT Fix this test using GCC, it currently times out.
// UNSUPPORTED: gcc-12
// This test requires the dylib support introduced in D92214.
// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{.+}}
// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx11.{{.+}}
// <format>
// template<class T, class charT = char>
// requires same_as<remove_cvref_t<T>, T> && formattable<T, charT>
// class range_formatter
// constexpr void set_separator(basic_string_view<charT> sep);
// Note this tests the basics of this function. It's tested in more detail in
// the format functions test.
#include <format>
#include <cassert>
#include <type_traits>
#include <vector>
#include "make_string.h"
#include "test_format_context.h"
#define SV(S) MAKE_STRING_VIEW(CharT, S)
template <class CharT>
constexpr void test_setter() {
std::range_formatter<int, CharT> formatter;
formatter.set_separator(SV("sep"));
// Note there is no direct way to validate this function modified the object.
if (!std::is_constant_evaluated()) {
using String = std::basic_string<CharT>;
using OutIt = std::back_insert_iterator<String>;
using FormatCtxT = std::basic_format_context<OutIt, CharT>;
String result;
OutIt out = std::back_inserter(result);
FormatCtxT format_ctx = test_format_context_create<OutIt, CharT>(out, std::make_format_args<FormatCtxT>());
formatter.format(std::vector<int>{0, 42, 99}, format_ctx);
assert(result == SV("[0sep42sep99]"));
}
}
constexpr bool test() {
test_setter<char>();
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
test_setter<wchar_t>();
#endif
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,58 @@
//===----------------------------------------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-has-no-incomplete-format
// TODO FMT Fix this test using GCC, it currently times out.
// UNSUPPORTED: gcc-12
// This test requires the dylib support introduced in D92214.
// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{.+}}
// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx11.{{.+}}
// <format>
// template<class T, class charT = char>
// requires same_as<remove_cvref_t<T>, T> && formattable<T, charT>
// class range_formatter
// constexpr formatter<T, charT>& underlying();
// constexpr const formatter<T, charT>& underlying() const;
#include <concepts>
#include <format>
#include "test_macros.h"
template <class CharT>
constexpr void test_underlying() {
{
std::range_formatter<int, CharT> formatter;
[[maybe_unused]] std::same_as<std::formatter<int, CharT>&> decltype(auto) underlying = formatter.underlying();
}
{
const std::range_formatter<int, CharT> formatter;
[[maybe_unused]] std::same_as<const std::formatter<int, CharT>&> decltype(auto) underlying = formatter.underlying();
}
}
constexpr bool test() {
test_underlying<char>();
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
test_underlying<wchar_t>();
#endif
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -8,6 +8,9 @@
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-has-no-incomplete-format
// TODO FMT Fix this test using GCC, it currently times out.
// UNSUPPORTED: gcc-12
// This test requires the dylib support introduced in D92214.
// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{.+}}
// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx11.{{.+}}

View File

@ -279,7 +279,7 @@ template <class CharT, class TestFunction, class ExceptionTest, class Nested>
void test_nested(TestFunction check, ExceptionTest check_exception, Nested&& input) {
// [format.formatter.spec]/2
// A debug-enabled specialization of formatter additionally provides a
// public, constexpr, non-static member function set_­debug_­format()
// public, constexpr, non-static member function set_debug_format()
// which modifies the state of the formatter to be as if the type of the
// std-format-spec parsed by the last call to parse were ?.
// pair and tuple are not debug-enabled specializations to the

View File

@ -13,6 +13,7 @@
#include <algorithm>
#include <charconv>
#include <format>
#include <ranges>
#include "make_string.h"
@ -129,4 +130,57 @@ private:
}
};
// Creates format string for the invalid types.
//
// valid contains a list of types that are valid.
// - The type ?s is the only type requiring 2 characters, use S for that type.
// - Whether n is a type or not depends on the context, is is always used.
//
// The return value is a collection of basic_strings, instead of
// basic_string_views since the values are temporaries.
namespace detail {
template <class CharT, size_t N>
std::basic_string<CharT> get_colons() {
static std::basic_string<CharT> result(N, CharT(':'));
return result;
}
constexpr std::string_view get_format_types() {
return "aAbBcdeEfFgGopsxX"
#if TEST_STD_VER > 20
"?"
#endif
;
}
template <class CharT, /*format_types types,*/ size_t N>
std::vector<std::basic_string<CharT>> fmt_invalid_types(std::string_view valid) {
// std::ranges::to is not available in C++20.
std::vector<std::basic_string<CharT>> result;
std::ranges::copy(
get_format_types() | std::views::filter([&](char type) { return valid.find(type) == std::string_view::npos; }) |
std::views::transform([&](char type) { return std::format(SV("{{{}{}}}"), get_colons<CharT, N>(), type); }),
std::back_inserter(result));
return result;
}
} // namespace detail
// Creates format string for the invalid types.
//
// valid contains a list of types that are valid.
//
// The return value is a collection of basic_strings, instead of
// basic_string_views since the values are temporaries.
template <class CharT>
std::vector<std::basic_string<CharT>> fmt_invalid_types(std::string_view valid) {
return detail::fmt_invalid_types<CharT, 1>(valid);
}
// Like fmt_invalid_types but when the format spec is for an underlying formatter.
template <class CharT>
std::vector<std::basic_string<CharT>> fmt_invalid_nested_types(std::string_view valid) {
return detail::fmt_invalid_types<CharT, 2>(valid);
}
#endif // TEST_SUPPORT_FORMAT_FUNCTIONS_COMMON_H