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 @@ -12,8 +12,10 @@ #include <__config> #include <__format/formatter.h> // for __char_type TODO FMT Move the concept? +#include <__iterator/back_insert_iterator.h> #include <__iterator/concepts.h> #include <__utility/move.h> +#include #include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) @@ -84,6 +86,86 @@ _OutIt __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 replace by a call to \c _Container::insert. +/// +/// \note a \a _Container needs to opt-in to 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; +}; + +/// A buffer that wraps a \ref back_insert_iterator for a \a _Container. +template +class _LIBCPP_TEMPLATE_VIS __container_buffer { +public: + using _CharT = typename _Container::value_type; + + _LIBCPP_HIDE_FROM_ABI explicit __container_buffer( + back_insert_iterator<_Container> __it) + : __container_(__it.__get_container()) {} + + _LIBCPP_HIDE_FROM_ABI void put(_CharT __c) { + *__buffer_it_++ = __c; + // Profiling showed flushing after adding is more efficient than flushing + // when entering the function. + if (__buffer_it_ == __buffer_.end()) + __flush(); + } + + _LIBCPP_HIDE_FROM_ABI auto out() { + __flush(); + return back_inserter(*__container_); + } + +private: + /// The size of the internal buffer. + /// + /// Profiling shows a 256 byte buffer seems optimal. + /// (256 elements for a char buffer and 64 elements for a 4-byte wchar_t) + static constexpr size_t __buffer_size = 256 / sizeof(_CharT); + + array<_CharT, __buffer_size> __buffer_; + _CharT* __buffer_it_ = __buffer_.begin(); + _Container* __container_; + + void __flush() { + // Use __buffer_it_ instead of end() since the buffer might be partly filled. + __container_->insert(__container_->end(), __buffer_.begin(), __buffer_it_); + __buffer_it_ = __buffer_.begin(); + } +}; + +/// Selects the type of the buffer for the output iterator. +template +class _LIBCPP_TEMPLATE_VIS __buffer_selector { + using _Iterator = typename __back_insert_iterator_container<_OutIt>::type; + +public: + using type = conditional_t, + __iterator_wrapper_buffer<_OutIt, _CharT>, + __container_buffer<_Iterator>>; +}; + } // namespace __format #endif // !defined(_LIBCPP_HAS_NO_CONCEPTS) 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_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 back_insert_iterator& operator*() {return *this;} _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 back_insert_iterator& operator++() {return *this;} _LIBCPP_INLINE_VISIBILITY _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 @@ -1272,6 +1272,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/format b/libcxx/include/format --- a/libcxx/include/format +++ b/libcxx/include/format @@ -496,7 +496,7 @@ _OutIt __out_it, basic_string_view<_CharT> __fmt, basic_format_args> __args) { - using _Buffer = __format::__iterator_wrapper_buffer<_OutIt, _CharT>; + using _Buffer = typename __format::__buffer_selector<_OutIt, _CharT>::type; _Buffer __buffer{_VSTD::move(__out_it)}; _VSTD::__format::__vformat_to( basic_format_parse_context{__fmt, __args.__size()}, @@ -626,7 +626,7 @@ _OutIt __out_it, locale __loc, basic_string_view<_CharT> __fmt, basic_format_args> __args) { - using _Buffer = __format::__iterator_wrapper_buffer<_OutIt, _CharT>; + using _Buffer = typename __format::__buffer_selector<_OutIt, _CharT>::type; _Buffer __buffer{_VSTD::move(__out_it)}; _VSTD::__format::__vformat_to( basic_format_parse_context{__fmt, __args.__size()}, 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 @@ -689,6 +689,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 @@ -496,6 +496,7 @@ typedef __wrap_iter const_iterator; typedef _VSTD::reverse_iterator 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,123 @@ +//===----------------------------------------------------------------------===// +// 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>); + +struct UserVector : public std::vector { + using std::vector::vector; +}; +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>); +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>);