diff --git a/libcxx/include/__format/buffer.h b/libcxx/include/__format/buffer.h --- a/libcxx/include/__format/buffer.h +++ b/libcxx/include/__format/buffer.h @@ -159,13 +159,65 @@ _OutIt __out_it_; }; +/// Concept to see whether a \a _Container is insertable. +/// +/// The concept is used to validate whether multiple calls to a +/// \ref back_insert_iterator can be replaced by a call to \c _Container::insert. +/// +/// \note a \a _Container needs to opt into the concept by adding a typedef +/// \ref __enable_format_insertable with the same type as \a _Container. +template +concept __insertable = + same_as<_Container, typename _Container::__enable_format_insertable> && + __formatter::__char_type && + requires(_Container& __t, typename _Container::value_type* __first, + typename _Container::value_type* __last) { + __t.insert(__t.end(), __first, __last); +}; + +/// Extract the container type of a \ref back_insert_iterator. +template +struct _LIBCPP_TEMPLATE_VIS __back_insert_iterator_container { + using type = void; +}; + +template <__insertable _Container> +struct _LIBCPP_TEMPLATE_VIS + __back_insert_iterator_container> { + using type = _Container; +}; + +/// Write policy for inserting the buffer in a container. +template +class _LIBCPP_TEMPLATE_VIS __writer_container { +public: + using _CharT = typename _Container::value_type; + + _LIBCPP_HIDE_FROM_ABI explicit __writer_container( + back_insert_iterator<_Container> __out_it) + : __container_{__out_it.__get_container()} {} + + _LIBCPP_HIDE_FROM_ABI auto out() { return back_inserter(*__container_); } + + _LIBCPP_HIDE_FROM_ABI void flush(_CharT* __ptr, size_t __size) { + __container_->insert(__container_->end(), __ptr, __ptr + __size); + } + +private: + _Container* __container_; +}; + /// Selects the type of the writer used for the output iterator. template class _LIBCPP_TEMPLATE_VIS __writer_selector { + using _Container = typename __back_insert_iterator_container<_OutIt>::type; + public: - using type = conditional_t<__enable_direct_output<_OutIt, _CharT>, - __writer_direct<_OutIt, _CharT>, - __writer_iterator<_OutIt, _CharT>>; + using type = + conditional_t, __writer_container<_Container>, + conditional_t<__enable_direct_output<_OutIt, _CharT>, + __writer_direct<_OutIt, _CharT>, + __writer_iterator<_OutIt, _CharT>>>; }; /// The generic formatting buffer. diff --git a/libcxx/include/__iterator/back_insert_iterator.h b/libcxx/include/__iterator/back_insert_iterator.h --- a/libcxx/include/__iterator/back_insert_iterator.h +++ b/libcxx/include/__iterator/back_insert_iterator.h @@ -55,6 +55,8 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 back_insert_iterator& operator*() {return *this;} _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 back_insert_iterator& operator++() {return *this;} _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 back_insert_iterator operator++(int) {return *this;} + // This is not part of this patch but D110573. + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 _Container* __get_container() const { return container; } }; template diff --git a/libcxx/include/deque b/libcxx/include/deque --- a/libcxx/include/deque +++ b/libcxx/include/deque @@ -1273,6 +1273,7 @@ typedef typename __base::const_pointer const_pointer; typedef _VSTD::reverse_iterator reverse_iterator; typedef _VSTD::reverse_iterator const_reverse_iterator; + typedef deque<_Tp, _Allocator> __enable_format_insertable; using typename __base::__deque_range; using typename __base::__deque_block_range; diff --git a/libcxx/include/list b/libcxx/include/list --- a/libcxx/include/list +++ b/libcxx/include/list @@ -864,6 +864,7 @@ #else typedef void __remove_return_type; #endif + typedef list<_Tp, _Alloc> __enable_format_insertable; _LIBCPP_INLINE_VISIBILITY list() diff --git a/libcxx/include/string b/libcxx/include/string --- a/libcxx/include/string +++ b/libcxx/include/string @@ -692,6 +692,7 @@ typedef const value_type& const_reference; typedef typename __alloc_traits::pointer pointer; typedef typename __alloc_traits::const_pointer const_pointer; + typedef basic_string<_CharT, _Traits, _Allocator> __enable_format_insertable; static_assert((!is_array::value), "Character type of basic_string must not be an array"); static_assert(( is_standard_layout::value), "Character type of basic_string must be standard-layout"); diff --git a/libcxx/include/vector b/libcxx/include/vector --- a/libcxx/include/vector +++ b/libcxx/include/vector @@ -366,7 +366,8 @@ typedef __wrap_iter iterator; typedef __wrap_iter const_iterator; typedef _VSTD::reverse_iterator reverse_iterator; - typedef _VSTD::reverse_iterator const_reverse_iterator; + typedef _VSTD::reverse_iterator const_reverse_iterator; + typedef vector<_Tp, _Allocator> __enable_format_insertable; static_assert((is_same::value), "Allocator::value_type must be same type as value_type"); diff --git a/libcxx/test/libcxx/utilities/format/enable_insertable.compile.pass.cpp b/libcxx/test/libcxx/utilities/format/enable_insertable.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/utilities/format/enable_insertable.compile.pass.cpp @@ -0,0 +1,132 @@ +//===----------------------------------------------------------------------===// +// 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 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-format + +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test_macros.h" + +template +struct no_value_type { + using __enable_format_insertable = no_value_type; + using iterator = CharT*; + iterator end(); + void insert(iterator, CharT*, CharT*); +}; + +template +struct no_end { + using __enable_format_insertable = no_end; + using value_type = CharT; + using iterator = CharT*; + void insert(iterator, CharT*, CharT*); +}; + +template +struct no_insert { + using __enable_format_insertable = no_insert; + using value_type = CharT; + using iterator = CharT*; + iterator end(); +}; + +template +struct no_specialization { + using value_type = CharT; + using iterator = CharT*; + iterator end(); + void insert(iterator, CharT*, CharT*); +}; + +template +struct valid { + using __enable_format_insertable = valid; + using value_type = CharT; + using iterator = CharT*; + iterator end(); + void insert(iterator, CharT*, CharT*); +}; + +static_assert(!std::__format::__insertable>); +static_assert(!std::__format::__insertable>); +static_assert(!std::__format::__insertable>); +static_assert(!std::__format::__insertable>); +static_assert(std::__format::__insertable>); + +static_assert(std::__format::__insertable); +static_assert(!std::__format::__insertable); + +static_assert(!std::__format::__insertable>); + +static_assert(std::__format::__insertable>); +static_assert(std::__format::__insertable>); +static_assert(!std::__format::__insertable>); +static_assert(std::__format::__insertable>); + +static_assert(!std::__format::__insertable>); +static_assert(!std::__format::__insertable>); +static_assert(!std::__format::__insertable>); +static_assert(!std::__format::__insertable>); + +static_assert(!std::__format::__insertable>); +static_assert(!std::__format::__insertable>); +static_assert(!std::__format::__insertable>); + +static_assert(!std::__format::__insertable>); + +#ifndef TEST_HAS_NO_WIDE_CHARACTERS +static_assert(!std::__format::__insertable>); +static_assert(!std::__format::__insertable>); +static_assert(!std::__format::__insertable>); +static_assert(!std::__format::__insertable>); +static_assert(std::__format::__insertable>); + +static_assert(std::__format::__insertable); +static_assert(!std::__format::__insertable); + +static_assert(!std::__format::__insertable>); + +static_assert(std::__format::__insertable>); +static_assert(std::__format::__insertable>); +static_assert(!std::__format::__insertable>); +static_assert(std::__format::__insertable>); + +static_assert(!std::__format::__insertable>); +static_assert(!std::__format::__insertable>); +static_assert(!std::__format::__insertable>); +static_assert(!std::__format::__insertable>); + +static_assert(!std::__format::__insertable>); +static_assert(!std::__format::__insertable>); +static_assert(!std::__format::__insertable>); + +static_assert(!std::__format::__insertable>); +#endif + +static_assert(!std::__format::__insertable>); +static_assert(!std::__format::__insertable>); + +struct UserVector : public std::vector { + using std::vector::vector; +}; +static_assert(!std::__format::__insertable);