diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -200,6 +200,7 @@ __filesystem/space_info.h __filesystem/u8path.h __format/buffer.h + __format/enable_insertable.h __format/format_arg.h __format/format_args.h __format/format_context.h 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 @@ -13,6 +13,7 @@ #include <__algorithm/copy_n.h> #include <__algorithm/unwrap_iter.h> #include <__config> +#include <__format/enable_insertable.h> #include <__format/formatter.h> // for __char_type TODO FMT Move the concept? #include <__iterator/back_insert_iterator.h> #include <__iterator/concepts.h> @@ -152,13 +153,58 @@ _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 replace by a call to \c _Container::insert. +/// +/// \note a \a _Container needs to opt-in to the concept by specializing +/// \ref __enable_insertable. +template +concept __insertable = + __enable_insertable<_Container> && __formatter::__char_type && + requires(_Container& __t, add_pointer_t __first, + add_pointer_t __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/__format/enable_insertable.h b/libcxx/include/__format/enable_insertable.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__format/enable_insertable.h @@ -0,0 +1,35 @@ +// -*- 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_ENABLE_INSERTABLE_H +#define _LIBCPP___FORMAT_ENABLE_INSERTABLE_H + +#include <__config> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 17 + +namespace __format { + +/// Opt-in to enable \ref __insertable for a \a _Container. +template +inline constexpr bool __enable_insertable = false; + +} // namespace __format + +#endif //_LIBCPP_STD_VER > 17 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___FORMAT_ENABLE_INSERTABLE_H diff --git a/libcxx/include/deque b/libcxx/include/deque --- a/libcxx/include/deque +++ b/libcxx/include/deque @@ -171,6 +171,7 @@ #include <__algorithm/unwrap_iter.h> #include <__assert> // all public C++ headers provide the assertion handler #include <__config> +#include <__format/enable_insertable.h> #include <__iterator/iterator_traits.h> #include <__split_buffer> #include <__utility/forward.h> @@ -3029,8 +3030,15 @@ __c.erase(_VSTD::remove_if(__c.begin(), __c.end(), __pred), __c.end()); return __old_size - __c.size(); } + +template <> +inline constexpr bool __format::__enable_insertable> = true; +#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS +template <> +inline constexpr bool __format::__enable_insertable> = true; #endif +#endif // _LIBCPP_STD_VER > 17 _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/format b/libcxx/include/format --- a/libcxx/include/format +++ b/libcxx/include/format @@ -127,6 +127,7 @@ #include <__config> #include <__debug> #include <__format/buffer.h> +#include <__format/enable_insertable.h> #include <__format/format_arg.h> #include <__format/format_args.h> #include <__format/format_context.h> diff --git a/libcxx/include/list b/libcxx/include/list --- a/libcxx/include/list +++ b/libcxx/include/list @@ -187,6 +187,7 @@ #include <__assert> // all public C++ headers provide the assertion handler #include <__config> #include <__debug> +#include <__format/enable_insertable.h> #include <__utility/forward.h> #include <__utility/move.h> #include <__utility/swap.h> @@ -2420,8 +2421,16 @@ erase(list<_Tp, _Allocator>& __c, const _Up& __v) { return _VSTD::erase_if(__c, [&](auto& __elem) { return __elem == __v; }); } + +template <> +inline constexpr bool __format::__enable_insertable> = true; +#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS +template <> +inline constexpr bool __format::__enable_insertable> = true; #endif +#endif // _LIBCPP_STD_VER > 17 + _LIBCPP_END_NAMESPACE_STD _LIBCPP_POP_MACROS diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -528,6 +528,7 @@ module __format { module buffer { private header "__format/buffer.h" } + module enable_insertable { private header "__format/enable_insertable.h" } module format_arg { private header "__format/format_arg.h" } module format_args { private header "__format/format_args.h" } module format_context { diff --git a/libcxx/include/string b/libcxx/include/string --- a/libcxx/include/string +++ b/libcxx/include/string @@ -525,6 +525,7 @@ #include <__assert> // all public C++ headers provide the assertion handler #include <__config> #include <__debug> +#include <__format/enable_insertable.h> #include <__ios/fpos.h> #include <__iterator/wrap_iter.h> #include <__utility/auto_cast.h> @@ -4489,6 +4490,16 @@ } } // namespace string_literals } // namespace literals + +#if _LIBCPP_STD_VER > 17 +template <> +inline constexpr bool __format::__enable_insertable> = true; +#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS +template <> +inline constexpr bool __format::__enable_insertable> = true; +#endif +#endif + #endif _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/vector b/libcxx/include/vector --- a/libcxx/include/vector +++ b/libcxx/include/vector @@ -283,6 +283,7 @@ #include <__bit_reference> #include <__config> #include <__debug> +#include <__format/enable_insertable.h> #include <__functional/hash.h> #include <__iterator/iterator_traits.h> #include <__iterator/wrap_iter.h> @@ -3304,8 +3305,16 @@ __c.erase(_VSTD::remove_if(__c.begin(), __c.end(), __pred), __c.end()); return __old_size - __c.size(); } + +template <> +inline constexpr bool __format::__enable_insertable> = true; +#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS +template <> +inline constexpr bool __format::__enable_insertable> = true; #endif +#endif // _LIBCPP_STD_VER > 17 + _LIBCPP_END_NAMESPACE_STD _LIBCPP_POP_MACROS diff --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp --- a/libcxx/test/libcxx/private_headers.verify.cpp +++ b/libcxx/test/libcxx/private_headers.verify.cpp @@ -232,6 +232,7 @@ #include <__filesystem/space_info.h> // expected-error@*:* {{use of private header from outside its module: '__filesystem/space_info.h'}} #include <__filesystem/u8path.h> // expected-error@*:* {{use of private header from outside its module: '__filesystem/u8path.h'}} #include <__format/buffer.h> // expected-error@*:* {{use of private header from outside its module: '__format/buffer.h'}} +#include <__format/enable_insertable.h> // expected-error@*:* {{use of private header from outside its module: '__format/enable_insertable.h'}} #include <__format/format_arg.h> // expected-error@*:* {{use of private header from outside its module: '__format/format_arg.h'}} #include <__format/format_args.h> // expected-error@*:* {{use of private header from outside its module: '__format/format_args.h'}} #include <__format/format_context.h> // expected-error@*:* {{use of private header from outside its module: '__format/format_context.h'}} 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,153 @@ +//===----------------------------------------------------------------------===// +// 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-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 iterator = CharT*; + iterator end(); + void insert(iterator, CharT*, CharT*); +}; + +template +struct no_end { + using value_type = CharT; + using iterator = CharT*; + void insert(iterator, CharT*, CharT*); +}; + +template +struct 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 value_type = CharT; + using iterator = CharT*; + iterator end(); + void insert(iterator, CharT*, CharT*); +}; + +namespace std::__format { +template <> +inline constexpr bool __enable_insertable> = true; +template <> +inline constexpr bool __enable_insertable> = true; +template <> +inline constexpr bool __enable_insertable> = true; +template <> +inline constexpr bool __enable_insertable> = true; +#ifndef TEST_HAS_NO_WIDE_CHARACTERS +template <> +inline constexpr bool __enable_insertable> = true; +template <> +inline constexpr bool __enable_insertable> = true; +template <> +inline constexpr bool __enable_insertable> = true; +template <> +inline constexpr bool __enable_insertable> = true; +#endif +} // namespace std::__format + +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>); +#endif + +namespace std::__format { +template <> +inline constexpr bool __enable_insertable> = true; +template <> +inline constexpr bool __enable_insertable> = true; +} // namespace std::__format + +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>); +#endif + +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>); +#endif + +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>); +#endif + +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>); +#endif + +static_assert(!std::__format::__insertable>); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS +static_assert(!std::__format::__insertable>); +#endif