master
  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_RANGE_DEFAULT_FORMATTER_H
 11#define _LIBCPP___FORMAT_RANGE_DEFAULT_FORMATTER_H
 12
 13#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 14#  pragma GCC system_header
 15#endif
 16
 17#include <__algorithm/ranges_copy.h>
 18#include <__chrono/statically_widen.h>
 19#include <__concepts/same_as.h>
 20#include <__config>
 21#include <__format/concepts.h>
 22#include <__format/formatter.h>
 23#include <__format/range_formatter.h>
 24#include <__iterator/back_insert_iterator.h>
 25#include <__ranges/concepts.h>
 26#include <__ranges/data.h>
 27#include <__ranges/from_range.h>
 28#include <__ranges/size.h>
 29#include <__type_traits/conditional.h>
 30#include <__type_traits/remove_cvref.h>
 31#include <__utility/pair.h>
 32#include <string_view>
 33
 34_LIBCPP_BEGIN_NAMESPACE_STD
 35
 36#if _LIBCPP_STD_VER >= 23
 37
 38template <class _Rp, class _CharT>
 39concept __const_formattable_range =
 40    ranges::input_range<const _Rp> && formattable<ranges::range_reference_t<const _Rp>, _CharT>;
 41
 42template <class _Rp, class _CharT>
 43using __fmt_maybe_const _LIBCPP_NODEBUG = conditional_t<__const_formattable_range<_Rp, _CharT>, const _Rp, _Rp>;
 44
 45_LIBCPP_DIAGNOSTIC_PUSH
 46_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wshadow")
 47_LIBCPP_GCC_DIAGNOSTIC_IGNORED("-Wshadow")
 48// This shadows map, set, and string.
 49enum class range_format { disabled, map, set, sequence, string, debug_string };
 50_LIBCPP_DIAGNOSTIC_POP
 51
 52// There is no definition of this struct, it's purely intended to be used to
 53// generate diagnostics.
 54template <class _Rp>
 55struct __instantiated_the_primary_template_of_format_kind;
 56
 57template <class _Rp>
 58constexpr range_format format_kind = [] {
 59  // [format.range.fmtkind]/1
 60  // A program that instantiates the primary template of format_kind is ill-formed.
 61  static_assert(sizeof(_Rp) != sizeof(_Rp), "create a template specialization of format_kind for your type");
 62  return range_format::disabled;
 63}();
 64
 65template <ranges::input_range _Rp>
 66  requires same_as<_Rp, remove_cvref_t<_Rp>>
 67inline constexpr range_format format_kind<_Rp> = [] {
 68  // [format.range.fmtkind]/2
 69
 70  // 2.1 If same_as<remove_cvref_t<ranges::range_reference_t<R>>, R> is true,
 71  // Otherwise format_kind<R> is range_format::disabled.
 72  if constexpr (same_as<remove_cvref_t<ranges::range_reference_t<_Rp>>, _Rp>)
 73    return range_format::disabled;
 74  // 2.2 Otherwise, if the qualified-id R::key_type is valid and denotes a type:
 75  else if constexpr (requires { typename _Rp::key_type; }) {
 76    // 2.2.1 If the qualified-id R::mapped_type is valid and denotes a type ...
 77    if constexpr (requires { typename _Rp::mapped_type; } &&
 78                  // 2.2.1 ... If either U is a specialization of pair or U is a specialization
 79                  // of tuple and tuple_size_v<U> == 2
 80                  __fmt_pair_like<remove_cvref_t<ranges::range_reference_t<_Rp>>>)
 81      return range_format::map;
 82    else
 83      // 2.2.2 Otherwise format_kind<R> is range_format::set.
 84      return range_format::set;
 85  } else
 86    // 2.3 Otherwise, format_kind<R> is range_format::sequence.
 87    return range_format::sequence;
 88}();
 89
 90template <range_format _Kp, ranges::input_range _Rp, class _CharT>
 91struct __range_default_formatter;
 92
 93// Required specializations
 94
 95template <ranges::input_range _Rp, class _CharT>
 96struct __range_default_formatter<range_format::sequence, _Rp, _CharT> {
 97private:
 98  using __maybe_const_r _LIBCPP_NODEBUG = __fmt_maybe_const<_Rp, _CharT>;
 99  range_formatter<remove_cvref_t<ranges::range_reference_t<__maybe_const_r>>, _CharT> __underlying_;
100
101public:
102  _LIBCPP_HIDE_FROM_ABI constexpr void set_separator(basic_string_view<_CharT> __separator) noexcept {
103    __underlying_.set_separator(__separator);
104  }
105  _LIBCPP_HIDE_FROM_ABI constexpr void
106  set_brackets(basic_string_view<_CharT> __opening_bracket, basic_string_view<_CharT> __closing_bracket) noexcept {
107    __underlying_.set_brackets(__opening_bracket, __closing_bracket);
108  }
109
110  template <class _ParseContext>
111  _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
112    return __underlying_.parse(__ctx);
113  }
114
115  template <class _FormatContext>
116  _LIBCPP_HIDE_FROM_ABI typename _FormatContext::iterator
117  format(__maybe_const_r& __range, _FormatContext& __ctx) const {
118    return __underlying_.format(__range, __ctx);
119  }
120};
121
122template <ranges::input_range _Rp, class _CharT>
123struct __range_default_formatter<range_format::map, _Rp, _CharT> {
124private:
125  using __maybe_const_map _LIBCPP_NODEBUG = __fmt_maybe_const<_Rp, _CharT>;
126  using __element_type _LIBCPP_NODEBUG    = remove_cvref_t<ranges::range_reference_t<__maybe_const_map>>;
127  range_formatter<__element_type, _CharT> __underlying_;
128
129public:
130  _LIBCPP_HIDE_FROM_ABI constexpr __range_default_formatter()
131    requires(__fmt_pair_like<__element_type>)
132  {
133    __underlying_.set_brackets(_LIBCPP_STATICALLY_WIDEN(_CharT, "{"), _LIBCPP_STATICALLY_WIDEN(_CharT, "}"));
134    __underlying_.underlying().set_brackets({}, {});
135    __underlying_.underlying().set_separator(_LIBCPP_STATICALLY_WIDEN(_CharT, ": "));
136  }
137
138  template <class _ParseContext>
139  _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
140    return __underlying_.parse(__ctx);
141  }
142
143  template <class _FormatContext>
144  _LIBCPP_HIDE_FROM_ABI typename _FormatContext::iterator
145  format(__maybe_const_map& __range, _FormatContext& __ctx) const {
146    return __underlying_.format(__range, __ctx);
147  }
148};
149
150template <ranges::input_range _Rp, class _CharT>
151struct __range_default_formatter<range_format::set, _Rp, _CharT> {
152private:
153  using __maybe_const_set _LIBCPP_NODEBUG = __fmt_maybe_const<_Rp, _CharT>;
154  using __element_type _LIBCPP_NODEBUG    = remove_cvref_t<ranges::range_reference_t<__maybe_const_set>>;
155  range_formatter<__element_type, _CharT> __underlying_;
156
157public:
158  _LIBCPP_HIDE_FROM_ABI constexpr __range_default_formatter() {
159    __underlying_.set_brackets(_LIBCPP_STATICALLY_WIDEN(_CharT, "{"), _LIBCPP_STATICALLY_WIDEN(_CharT, "}"));
160  }
161
162  template <class _ParseContext>
163  _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
164    return __underlying_.parse(__ctx);
165  }
166
167  template <class _FormatContext>
168  _LIBCPP_HIDE_FROM_ABI typename _FormatContext::iterator
169  format(__maybe_const_set& __range, _FormatContext& __ctx) const {
170    return __underlying_.format(__range, __ctx);
171  }
172};
173
174template <range_format _Kp, ranges::input_range _Rp, class _CharT>
175  requires(_Kp == range_format::string || _Kp == range_format::debug_string)
176struct __range_default_formatter<_Kp, _Rp, _CharT> {
177private:
178  // This deviates from the Standard, there the exposition only type is
179  //   formatter<basic_string<charT>, charT> underlying_;
180  // Using a string_view allows the format function to avoid a copy of the
181  // input range when it is a contigious range.
182  formatter<basic_string_view<_CharT>, _CharT> __underlying_;
183
184public:
185  template <class _ParseContext>
186  _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
187    typename _ParseContext::iterator __i = __underlying_.parse(__ctx);
188    if constexpr (_Kp == range_format::debug_string)
189      __underlying_.set_debug_format();
190    return __i;
191  }
192
193  template <class _FormatContext>
194  _LIBCPP_HIDE_FROM_ABI typename _FormatContext::iterator
195  format(conditional_t<ranges::input_range<const _Rp>, const _Rp&, _Rp&> __range, _FormatContext& __ctx) const {
196    // When the range is contiguous use a basic_string_view instead to avoid a
197    // copy of the underlying data. The basic_string_view formatter
198    // specialization is the "basic" string formatter in libc++.
199    if constexpr (ranges::contiguous_range<_Rp> && std::ranges::sized_range<_Rp>)
200      return __underlying_.format(basic_string_view<_CharT>{ranges::data(__range), ranges::size(__range)}, __ctx);
201    else
202      return __underlying_.format(basic_string<_CharT>{from_range, __range}, __ctx);
203  }
204};
205
206template <ranges::input_range _Rp, class _CharT>
207  requires(format_kind<_Rp> != range_format::disabled && formattable<ranges::range_reference_t<_Rp>, _CharT>)
208struct formatter<_Rp, _CharT> : __range_default_formatter<format_kind<_Rp>, _Rp, _CharT> {};
209
210#endif // _LIBCPP_STD_VER >= 23
211
212_LIBCPP_END_NAMESPACE_STD
213
214#endif // _LIBCPP___FORMAT_RANGE_DEFAULT_FORMATTER_H