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