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
45template <class _OutIt, class _CharT>
46  requires output_iterator<_OutIt, const _CharT&>
47class _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 */
55template <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
63template <class _OutIt, class _CharT>
64_LIBCPP_HIDE_FROM_ABI basic_format_context<_OutIt, _CharT>
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
70using format_context = basic_format_context<back_insert_iterator<__format::__output_buffer<char>>, char>;
71#  ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
72using wformat_context = basic_format_context< back_insert_iterator<__format::__output_buffer<wchar_t>>, wchar_t>;
73#  endif
74
75template <class _OutIt, class _CharT>
76  requires output_iterator<_OutIt, const _CharT&>
77class
78    // clang-format off
79    _LIBCPP_TEMPLATE_VIS
80    _LIBCPP_PREFERRED_NAME(format_context)
81    _LIBCPP_IF_WIDE_CHARACTERS(_LIBCPP_PREFERRED_NAME(wformat_context))
82    // clang-format on
83    basic_format_context {
84public:
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
103private:
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.
153template <class _CharT>
154class _LIBCPP_TEMPLATE_VIS basic_format_context<typename __format::__retarget_buffer<_CharT>::__iterator, _CharT> {
155public:
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>
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
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
190  _LIBCPP_HIDE_FROM_ABI std::locale locale() { return __loc_(__ctx_); }
191#  endif
192  _LIBCPP_HIDE_FROM_ABI iterator out() { return std::move(__out_it_); }
193  _LIBCPP_HIDE_FROM_ABI void advance_to(iterator __it) { __out_it_ = std::move(__it); }
194
195private:
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