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
42template <class _OutIt, class _CharT>
43requires output_iterator<_OutIt, const _CharT&>
44class _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 */
52template <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
61template <class _OutIt, class _CharT>
62_LIBCPP_HIDE_FROM_ABI basic_format_context<_OutIt, _CharT>
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
70using format_context =
71    basic_format_context<back_insert_iterator<__format::__output_buffer<char>>,
72                         char>;
73#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
74using wformat_context = basic_format_context<
75    back_insert_iterator<__format::__output_buffer<wchar_t>>, wchar_t>;
76#endif
77
78template <class _OutIt, class _CharT>
79requires output_iterator<_OutIt, const _CharT&>
80class
81    // clang-format off
82    _LIBCPP_TEMPLATE_VIS
83    _LIBCPP_AVAILABILITY_FORMAT
84    _LIBCPP_PREFERRED_NAME(format_context)
85    _LIBCPP_IF_WIDE_CHARACTERS(_LIBCPP_PREFERRED_NAME(wformat_context))
86    // clang-format on
87    basic_format_context {
88public:
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
108private:
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.
164template <class _CharT>
165class _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT
166    basic_format_context<typename __format::__retarget_buffer<_CharT>::__iterator, _CharT> {
167public:
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>
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
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
202  _LIBCPP_HIDE_FROM_ABI _VSTD::locale locale() { return __loc_(__ctx_); }
203#  endif
204  _LIBCPP_HIDE_FROM_ABI iterator out() { return std::move(__out_it_); }
205  _LIBCPP_HIDE_FROM_ABI void advance_to(iterator __it) { __out_it_ = std::move(__it); }
206
207private:
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