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