xref: /freebsd/contrib/llvm-project/libcxx/include/__format/format_context.h (revision 700637cbb5e582861067a11aaca4d053546871d2)
1 // -*- C++ -*-
2 //===----------------------------------------------------------------------===//
3 //
4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 // See https://llvm.org/LICENSE.txt for license information.
6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #ifndef _LIBCPP___FORMAT_FORMAT_CONTEXT_H
11 #define _LIBCPP___FORMAT_FORMAT_CONTEXT_H
12 
13 #include <__concepts/same_as.h>
14 #include <__config>
15 #include <__format/buffer.h>
16 #include <__format/format_arg.h>
17 #include <__format/format_arg_store.h>
18 #include <__format/format_args.h>
19 #include <__format/format_error.h>
20 #include <__fwd/format.h>
21 #include <__iterator/back_insert_iterator.h>
22 #include <__iterator/concepts.h>
23 #include <__memory/addressof.h>
24 #include <__utility/move.h>
25 #include <__variant/monostate.h>
26 
27 #if _LIBCPP_HAS_LOCALIZATION
28 #  include <__locale>
29 #  include <optional>
30 #endif
31 
32 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
33 #  pragma GCC system_header
34 #endif
35 
36 _LIBCPP_PUSH_MACROS
37 #include <__undef_macros>
38 
39 _LIBCPP_BEGIN_NAMESPACE_STD
40 
41 #if _LIBCPP_STD_VER >= 20
42 
43 template <class _OutIt, class _CharT>
44   requires output_iterator<_OutIt, const _CharT&>
45 class basic_format_context;
46 
47 #  if _LIBCPP_HAS_LOCALIZATION
48 /**
49  * Helper to create a basic_format_context.
50  *
51  * This is needed since the constructor is private.
52  */
53 template <class _OutIt, class _CharT>
54 _LIBCPP_HIDE_FROM_ABI basic_format_context<_OutIt, _CharT>
55 __format_context_create(_OutIt __out_it,
56                         basic_format_args<basic_format_context<_OutIt, _CharT>> __args,
57                         optional<std::locale>&& __loc = nullopt) {
58   return std::basic_format_context(std::move(__out_it), __args, std::move(__loc));
59 }
60 #  else
61 template <class _OutIt, class _CharT>
62 _LIBCPP_HIDE_FROM_ABI basic_format_context<_OutIt, _CharT>
__format_context_create(_OutIt __out_it,basic_format_args<basic_format_context<_OutIt,_CharT>> __args)63 __format_context_create(_OutIt __out_it, basic_format_args<basic_format_context<_OutIt, _CharT>> __args) {
64   return std::basic_format_context(std::move(__out_it), __args);
65 }
66 #  endif
67 
68 using format_context = basic_format_context<back_insert_iterator<__format::__output_buffer<char>>, char>;
69 #  if _LIBCPP_HAS_WIDE_CHARACTERS
70 using wformat_context = basic_format_context< back_insert_iterator<__format::__output_buffer<wchar_t>>, wchar_t>;
71 #  endif
72 
73 template <class _OutIt, class _CharT>
74   requires output_iterator<_OutIt, const _CharT&>
_LIBCPP_PREFERRED_NAME(format_context)75 class _LIBCPP_PREFERRED_NAME(format_context)
76     _LIBCPP_IF_WIDE_CHARACTERS(_LIBCPP_PREFERRED_NAME(wformat_context)) basic_format_context {
77 public:
78   using iterator  = _OutIt;
79   using char_type = _CharT;
80   template <class _Tp>
81   using formatter_type = formatter<_Tp, _CharT>;
82 
83   _LIBCPP_HIDE_FROM_ABI basic_format_arg<basic_format_context> arg(size_t __id) const noexcept {
84     return __args_.get(__id);
85   }
86 #  if _LIBCPP_HAS_LOCALIZATION
87   _LIBCPP_HIDE_FROM_ABI std::locale locale() {
88     if (!__loc_)
89       __loc_ = std::locale{};
90     return *__loc_;
91   }
92 #  endif
93   _LIBCPP_HIDE_FROM_ABI iterator out() { return std::move(__out_it_); }
94   _LIBCPP_HIDE_FROM_ABI void advance_to(iterator __it) { __out_it_ = std::move(__it); }
95 
96 private:
97   iterator __out_it_;
98   basic_format_args<basic_format_context> __args_;
99 #  if _LIBCPP_HAS_LOCALIZATION
100 
101   // The Standard doesn't specify how the locale is stored.
102   // [format.context]/6
103   // std::locale locale();
104   //   Returns: The locale passed to the formatting function if the latter
105   //   takes one, and std::locale() otherwise.
106   // This is done by storing the locale of the constructor in this optional. If
107   // locale() is called and the optional has no value the value will be created.
108   // This allows the implementation to lazily create the locale.
109   // TODO FMT Validate whether lazy creation is the best solution.
110   optional<std::locale> __loc_;
111 
112   template <class _OtherOutIt, class _OtherCharT>
113   friend _LIBCPP_HIDE_FROM_ABI basic_format_context<_OtherOutIt, _OtherCharT> __format_context_create(
114       _OtherOutIt, basic_format_args<basic_format_context<_OtherOutIt, _OtherCharT>>, optional<std::locale>&&);
115 
116   // Note: the Standard doesn't specify the required constructors.
117   _LIBCPP_HIDE_FROM_ABI explicit basic_format_context(
118       _OutIt __out_it, basic_format_args<basic_format_context> __args, optional<std::locale>&& __loc)
119       : __out_it_(std::move(__out_it)), __args_(__args), __loc_(std::move(__loc)) {}
120 #  else
121   template <class _OtherOutIt, class _OtherCharT>
122   friend _LIBCPP_HIDE_FROM_ABI basic_format_context<_OtherOutIt, _OtherCharT>
123       __format_context_create(_OtherOutIt, basic_format_args<basic_format_context<_OtherOutIt, _OtherCharT>>);
124 
125   _LIBCPP_HIDE_FROM_ABI explicit basic_format_context(_OutIt __out_it, basic_format_args<basic_format_context> __args)
126       : __out_it_(std::move(__out_it)), __args_(__args) {}
127 #  endif
128 
129 public:
130   basic_format_context(const basic_format_context&)            = delete;
131   basic_format_context& operator=(const basic_format_context&) = delete;
132 };
133 
134 // A specialization for __retarget_buffer
135 //
136 // See __retarget_buffer for the motivation for this specialization.
137 //
138 // This context holds a reference to the instance of the basic_format_context
139 // that is retargeted. It converts a formatting argument when it is requested
140 // during formatting. It is expected that the usage of the arguments is rare so
141 // the lookups are not expected to be used often. An alternative would be to
142 // convert all elements during construction.
143 //
144 // The elements of the retargets context are only used when an underlying
145 // formatter uses a locale specific formatting or an formatting argument is
146 // part for the format spec. For example
147 //   format("{:256:{}}", input, 8);
148 // Here the width of an element in input is determined dynamically.
149 // Note when the top-level element has no width the retargeting is not needed.
150 template <class _CharT>
151 class basic_format_context<typename __format::__retarget_buffer<_CharT>::__iterator, _CharT> {
152 public:
153   using iterator  = typename __format::__retarget_buffer<_CharT>::__iterator;
154   using char_type = _CharT;
155   template <class _Tp>
156   using formatter_type = formatter<_Tp, _CharT>;
157 
158   template <class _Context>
basic_format_context(iterator __out_it,_Context & __ctx)159   _LIBCPP_HIDE_FROM_ABI explicit basic_format_context(iterator __out_it, _Context& __ctx)
160       : __out_it_(std::move(__out_it)),
161 #  if _LIBCPP_HAS_LOCALIZATION
162         __loc_([](void* __c) { return static_cast<_Context*>(__c)->locale(); }),
163 #  endif
164         __ctx_(std::addressof(__ctx)),
165         __arg_([](void* __c, size_t __id) {
166           auto __visitor = [&](auto __arg) -> basic_format_arg<basic_format_context> {
167             if constexpr (same_as<decltype(__arg), monostate>)
168               return {};
169             else if constexpr (same_as<decltype(__arg), typename basic_format_arg<_Context>::handle>)
170               // At the moment it's not possible for formatting to use a re-targeted handle.
171               // TODO FMT add this when support is needed.
172               std::__throw_format_error("Re-targeting handle not supported");
173             else
174               return basic_format_arg<basic_format_context>{
175                   __format::__determine_arg_t<basic_format_context, decltype(__arg)>(),
176                   __basic_format_arg_value<basic_format_context>(__arg)};
177           };
178 #  if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPLICIT_THIS_PARAMETER
179           return static_cast<_Context*>(__c)->arg(__id).visit(std::move(__visitor));
180 #  else
181           _LIBCPP_SUPPRESS_DEPRECATED_PUSH
182           return std::visit_format_arg(std::move(__visitor), static_cast<_Context*>(__c)->arg(__id));
183           _LIBCPP_SUPPRESS_DEPRECATED_POP
184 #  endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPLICIT_THIS_PARAMETER
185         }) {
186   }
187 
arg(size_t __id)188   _LIBCPP_HIDE_FROM_ABI basic_format_arg<basic_format_context> arg(size_t __id) const noexcept {
189     return __arg_(__ctx_, __id);
190   }
191 #  if _LIBCPP_HAS_LOCALIZATION
locale()192   _LIBCPP_HIDE_FROM_ABI std::locale locale() { return __loc_(__ctx_); }
193 #  endif
out()194   _LIBCPP_HIDE_FROM_ABI iterator out() { return std::move(__out_it_); }
advance_to(iterator __it)195   _LIBCPP_HIDE_FROM_ABI void advance_to(iterator __it) { __out_it_ = std::move(__it); }
196 
197 private:
198   iterator __out_it_;
199 
200 #  if _LIBCPP_HAS_LOCALIZATION
201   std::locale (*__loc_)(void* __ctx);
202 #  endif
203 
204   void* __ctx_;
205   basic_format_arg<basic_format_context> (*__arg_)(void* __ctx, size_t __id);
206 };
207 
208 _LIBCPP_CTAD_SUPPORTED_FOR_TYPE(basic_format_context);
209 #endif // _LIBCPP_STD_VER >= 20
210 
211 _LIBCPP_END_NAMESPACE_STD
212 
213 _LIBCPP_POP_MACROS
214 
215 #endif // _LIBCPP___FORMAT_FORMAT_CONTEXT_H
216