diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -128,6 +128,7 @@ __config __debug __errc + __format/buffer.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 new file mode 100644 --- /dev/null +++ b/libcxx/include/__format/buffer.h @@ -0,0 +1,94 @@ +// -*- 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_BUFFER_H +#define _LIBCPP___FORMAT_BUFFER_H + +#include <__config> +#include <__format/formatter.h> // for __char_type TODO FMT Move the concept? +#include <__iterator/concepts.h> +#include <__utility/move.h> +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 17 + +// TODO FMT Remove this once we require compilers with proper C++20 support. +// If the compiler has no concepts support, the format header will be disabled. +// Without concepts support enable_if needs to be used and that too much effort +// to support compilers with partial C++20 support. +#if !defined(_LIBCPP_HAS_NO_CONCEPTS) + +namespace __format { + +/// The output iterator used for the @ref format_context. +/// +/// In order to specialize the iterator only for the character type used it +/// uses a type-erased function pointer to call the underlying buffer storage +/// function. +template <__formatter::__char_type _CharT> +class _LIBCPP_TEMPLATE_VIS __output_iterator { +public: + // No Legacy tag since the code isn't available prior to C++20. + using value_type = void; + using difference_type = ptrdiff_t; + using pointer = void; + using reference = void; + + template + _LIBCPP_HIDE_FROM_ABI explicit __output_iterator(_Tp* __obj) + : __fun_([](_CharT __c, void* __o) { static_cast<_Tp*>(__o)->put(__c); }), + __obj_(__obj) {} + + _LIBCPP_HIDE_FROM_ABI __output_iterator& operator=(_CharT __c) { + __fun_(__c, __obj_); + return *this; + } + _LIBCPP_HIDE_FROM_ABI __output_iterator& operator*() { return *this; } + _LIBCPP_HIDE_FROM_ABI __output_iterator& operator++() { return *this; } + _LIBCPP_HIDE_FROM_ABI __output_iterator& operator++(int) { return *this; } + +private: + void (*__fun_)(_CharT, void*); + void* __obj_; +}; + +/// A "buffer" that directly writes to the supplied iterator. +/// +/// This helper is specialized for all different output iterators used in the +/// format function. Together with the @ref __output_iterator this offers a +/// type erasure, reducing the number of formatter specializations. +template +requires(output_iterator<_OutIt, const _CharT&>) class _LIBCPP_TEMPLATE_VIS + __iterator_wrapper_buffer { +public: + _LIBCPP_HIDE_FROM_ABI explicit __iterator_wrapper_buffer(_OutIt __it) + : __it_(_VSTD::move(__it)) {} + + _LIBCPP_HIDE_FROM_ABI void put(_CharT __c) { *__it_++ = __c; } + + _LIBCPP_HIDE_FROM_ABI _OutIt out() { return __it_; } + +private: + _OutIt __it_; +}; + +} // namespace __format + +#endif // !defined(_LIBCPP_HAS_NO_CONCEPTS) +#endif //_LIBCPP_STD_VER > 17 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___FORMAT_BUFFER_H diff --git a/libcxx/include/__format/format_context.h b/libcxx/include/__format/format_context.h --- a/libcxx/include/__format/format_context.h +++ b/libcxx/include/__format/format_context.h @@ -12,12 +12,11 @@ #include <__availability> #include <__config> +#include <__format/buffer.h> #include <__format/format_args.h> #include <__format/format_fwd.h> #include <__iterator/concepts.h> #include -#include -#include #ifndef _LIBCPP_HAS_NO_LOCALIZATION #include @@ -145,9 +144,10 @@ // storage by wrapping those in temporary objects with a uniform interface // (such as a span) and polymorphic reallocation. - end note] -using format_context = basic_format_context, char>; +using format_context = + basic_format_context<__format::__output_iterator, char>; using wformat_context = - basic_format_context, wchar_t>; + basic_format_context<__format::__output_iterator, wchar_t>; #endif // !defined(_LIBCPP_HAS_NO_CONCEPTS) diff --git a/libcxx/include/format b/libcxx/include/format --- a/libcxx/include/format +++ b/libcxx/include/format @@ -266,6 +266,7 @@ #include <__config> #include <__debug> +#include <__format/buffer.h> #include <__format/format_arg.h> #include <__format/format_args.h> #include <__format/format_context.h> @@ -495,11 +496,15 @@ _OutIt __out_it, basic_string_view<_CharT> __fmt, basic_format_args> __args) { - basic_string<_CharT> __str; + using _Buffer = __format::__iterator_wrapper_buffer<_OutIt, _CharT>; + _Buffer __buffer{_VSTD::move(__out_it)}; _VSTD::__format::__vformat_to( basic_format_parse_context{__fmt, __args.__size()}, - _VSTD::__format_context_create(_VSTD::back_inserter(__str), __args)); - return _VSTD::copy_n(__str.begin(), __str.size(), _VSTD::move(__out_it)); + _VSTD::__format_context_create( + _VSTD::__format::__output_iterator<_CharT>{ + _VSTD::addressof(__buffer)}, + __args)); + return __buffer.out(); } template @@ -621,12 +626,15 @@ _OutIt __out_it, locale __loc, basic_string_view<_CharT> __fmt, basic_format_args> __args) { - basic_string<_CharT> __str; + using _Buffer = __format::__iterator_wrapper_buffer<_OutIt, _CharT>; + _Buffer __buffer{_VSTD::move(__out_it)}; _VSTD::__format::__vformat_to( basic_format_parse_context{__fmt, __args.__size()}, - _VSTD::__format_context_create(_VSTD::back_inserter(__str), __args, - _VSTD::move(__loc))); - return _VSTD::copy_n(__str.begin(), __str.size(), _VSTD::move(__out_it)); + _VSTD::__format_context_create( + _VSTD::__format::__output_iterator<_CharT>{ + _VSTD::addressof(__buffer)}, + __args, _VSTD::move(__loc))); + return __buffer.out(); } template diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -438,6 +438,7 @@ export optional export locale } + module buffer { private header "__format/buffer.h" } module format_error { private header "__format/format_error.h" } module format_fwd { private header "__format/format_fwd.h" } module format_parse_context { private header "__format/format_parse_context.h" } diff --git a/libcxx/test/libcxx/diagnostics/detail.headers/format/buffer.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/format/buffer.module.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/diagnostics/detail.headers/format/buffer.module.verify.cpp @@ -0,0 +1,16 @@ +// -*- 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: modules-build + +// WARNING: This test was generated by 'generate_private_header_tests.py' +// and should not be edited manually. + +// expected-error@*:* {{use of private header from outside its module: '__format/buffer.h'}} +#include <__format/buffer.h> diff --git a/libcxx/test/std/utilities/format/format.formatter/format.context/types.compile.pass.cpp b/libcxx/test/libcxx/utilities/format/format.formatter/format.context/types.compile.pass.cpp rename from libcxx/test/std/utilities/format/format.formatter/format.context/types.compile.pass.cpp rename to libcxx/test/libcxx/utilities/format/format.formatter/format.context/types.compile.pass.cpp --- a/libcxx/test/std/utilities/format/format.formatter/format.context/types.compile.pass.cpp +++ b/libcxx/test/libcxx/utilities/format/format.formatter/format.context/types.compile.pass.cpp @@ -25,7 +25,6 @@ // using wformat_context = basic_format_context; #include -#include #include #include @@ -98,22 +97,15 @@ } constexpr void test() { - test>, char>(); - test>, wchar_t>(); - test>, char8_t>(); - test>, char16_t>(); - test>, char32_t>(); + test, char>(); + test, wchar_t>(); } -static_assert(std::is_same_v< - std::format_context, - std::basic_format_context< - std::back_insert_iterator>, char>>); static_assert( - std::is_same_v< - std::wformat_context, - std::basic_format_context< - std::back_insert_iterator>, wchar_t>>); - -// Required for MSVC internal test runner compatibility. -int main(int, char**) { return 0; } + std::is_same_v, char>>); +static_assert( + std::is_same_v, wchar_t>>);