1311116Sdim//===- FormatVariadicDetails.h - Helpers for FormatVariadic.h ----*- C++-*-===//
2311116Sdim//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6311116Sdim//
7311116Sdim//===----------------------------------------------------------------------===//
8311116Sdim
9311116Sdim#ifndef LLVM_SUPPORT_FORMATVARIADIC_DETAILS_H
10311116Sdim#define LLVM_SUPPORT_FORMATVARIADIC_DETAILS_H
11311116Sdim
12311116Sdim#include "llvm/ADT/StringRef.h"
13311116Sdim#include "llvm/Support/raw_ostream.h"
14311116Sdim
15311116Sdim#include <type_traits>
16311116Sdim
17311116Sdimnamespace llvm {
18311116Sdimtemplate <typename T, typename Enable = void> struct format_provider {};
19341825Sdimclass Error;
20311116Sdim
21311116Sdimnamespace detail {
22311116Sdimclass format_adapter {
23344779Sdim  virtual void anchor();
24344779Sdim
25311116Sdimprotected:
26311116Sdim  virtual ~format_adapter() {}
27311116Sdim
28311116Sdimpublic:
29311116Sdim  virtual void format(raw_ostream &S, StringRef Options) = 0;
30311116Sdim};
31311116Sdim
32311116Sdimtemplate <typename T> class provider_format_adapter : public format_adapter {
33311116Sdim  T Item;
34311116Sdim
35311116Sdimpublic:
36327952Sdim  explicit provider_format_adapter(T &&Item) : Item(std::forward<T>(Item)) {}
37311116Sdim
38311116Sdim  void format(llvm::raw_ostream &S, StringRef Options) override {
39311116Sdim    format_provider<typename std::decay<T>::type>::format(Item, S, Options);
40311116Sdim  }
41311116Sdim};
42311116Sdim
43341825Sdimtemplate <typename T>
44341825Sdimclass stream_operator_format_adapter : public format_adapter {
45341825Sdim  T Item;
46341825Sdim
47341825Sdimpublic:
48341825Sdim  explicit stream_operator_format_adapter(T &&Item)
49341825Sdim      : Item(std::forward<T>(Item)) {}
50341825Sdim
51341825Sdim  void format(llvm::raw_ostream &S, StringRef Options) override { S << Item; }
52341825Sdim};
53341825Sdim
54311116Sdimtemplate <typename T> class missing_format_adapter;
55311116Sdim
56311116Sdim// Test if format_provider<T> is defined on T and contains a member function
57311116Sdim// with the signature:
58311116Sdim//   static void format(const T&, raw_stream &, StringRef);
59311116Sdim//
60311116Sdimtemplate <class T> class has_FormatProvider {
61311116Sdimpublic:
62311116Sdim  using Decayed = typename std::decay<T>::type;
63311116Sdim  typedef void (*Signature_format)(const Decayed &, llvm::raw_ostream &,
64311116Sdim                                   StringRef);
65311116Sdim
66311116Sdim  template <typename U>
67311116Sdim  static char test(SameType<Signature_format, &U::format> *);
68311116Sdim
69311116Sdim  template <typename U> static double test(...);
70311116Sdim
71311116Sdim  static bool const value =
72311116Sdim      (sizeof(test<llvm::format_provider<Decayed>>(nullptr)) == 1);
73311116Sdim};
74311116Sdim
75341825Sdim// Test if raw_ostream& << T -> raw_ostream& is findable via ADL.
76341825Sdimtemplate <class T> class has_StreamOperator {
77341825Sdimpublic:
78341825Sdim  using ConstRefT = const typename std::decay<T>::type &;
79341825Sdim
80341825Sdim  template <typename U>
81341825Sdim  static char test(typename std::enable_if<
82341825Sdim                   std::is_same<decltype(std::declval<llvm::raw_ostream &>()
83341825Sdim                                         << std::declval<U>()),
84341825Sdim                                llvm::raw_ostream &>::value,
85341825Sdim                   int *>::type);
86341825Sdim
87341825Sdim  template <typename U> static double test(...);
88341825Sdim
89341825Sdim  static bool const value = (sizeof(test<ConstRefT>(nullptr)) == 1);
90341825Sdim};
91341825Sdim
92311116Sdim// Simple template that decides whether a type T should use the member-function
93311116Sdim// based format() invocation.
94311116Sdimtemplate <typename T>
95311116Sdimstruct uses_format_member
96311116Sdim    : public std::integral_constant<
97311116Sdim          bool,
98311116Sdim          std::is_base_of<format_adapter,
99311116Sdim                          typename std::remove_reference<T>::type>::value> {};
100311116Sdim
101311116Sdim// Simple template that decides whether a type T should use the format_provider
102311116Sdim// based format() invocation.  The member function takes priority, so this test
103311116Sdim// will only be true if there is not ALSO a format member.
104311116Sdimtemplate <typename T>
105311116Sdimstruct uses_format_provider
106311116Sdim    : public std::integral_constant<
107311116Sdim          bool, !uses_format_member<T>::value && has_FormatProvider<T>::value> {
108311116Sdim};
109311116Sdim
110341825Sdim// Simple template that decides whether a type T should use the operator<<
111341825Sdim// based format() invocation.  This takes last priority.
112341825Sdimtemplate <typename T>
113341825Sdimstruct uses_stream_operator
114341825Sdim    : public std::integral_constant<bool, !uses_format_member<T>::value &&
115341825Sdim                                              !uses_format_provider<T>::value &&
116341825Sdim                                              has_StreamOperator<T>::value> {};
117341825Sdim
118311116Sdim// Simple template that decides whether a type T has neither a member-function
119311116Sdim// nor format_provider based implementation that it can use.  Mostly used so
120311116Sdim// that the compiler spits out a nice diagnostic when a type with no format
121311116Sdim// implementation can be located.
122311116Sdimtemplate <typename T>
123311116Sdimstruct uses_missing_provider
124341825Sdim    : public std::integral_constant<bool, !uses_format_member<T>::value &&
125341825Sdim                                              !uses_format_provider<T>::value &&
126341825Sdim                                              !uses_stream_operator<T>::value> {
127341825Sdim};
128311116Sdim
129311116Sdimtemplate <typename T>
130311116Sdimtypename std::enable_if<uses_format_member<T>::value, T>::type
131311116Sdimbuild_format_adapter(T &&Item) {
132311116Sdim  return std::forward<T>(Item);
133311116Sdim}
134311116Sdim
135311116Sdimtemplate <typename T>
136311116Sdimtypename std::enable_if<uses_format_provider<T>::value,
137311116Sdim                        provider_format_adapter<T>>::type
138311116Sdimbuild_format_adapter(T &&Item) {
139311116Sdim  return provider_format_adapter<T>(std::forward<T>(Item));
140311116Sdim}
141311116Sdim
142311116Sdimtemplate <typename T>
143341825Sdimtypename std::enable_if<uses_stream_operator<T>::value,
144341825Sdim                        stream_operator_format_adapter<T>>::type
145341825Sdimbuild_format_adapter(T &&Item) {
146341825Sdim  // If the caller passed an Error by value, then stream_operator_format_adapter
147341825Sdim  // would be responsible for consuming it.
148341825Sdim  // Make the caller opt into this by calling fmt_consume().
149341825Sdim  static_assert(
150341825Sdim      !std::is_same<llvm::Error, typename std::remove_cv<T>::type>::value,
151341825Sdim      "llvm::Error-by-value must be wrapped in fmt_consume() for formatv");
152341825Sdim  return stream_operator_format_adapter<T>(std::forward<T>(Item));
153341825Sdim}
154341825Sdim
155341825Sdimtemplate <typename T>
156311116Sdimtypename std::enable_if<uses_missing_provider<T>::value,
157311116Sdim                        missing_format_adapter<T>>::type
158311116Sdimbuild_format_adapter(T &&Item) {
159311116Sdim  return missing_format_adapter<T>();
160311116Sdim}
161311116Sdim}
162311116Sdim}
163311116Sdim
164311116Sdim#endif
165