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