1//===- FormatVariadic.h - Efficient type-safe string formatting --*- C++-*-===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file implements the formatv() function which can be used with other LLVM
11// subsystems to provide printf-like formatting, but with improved safety and
12// flexibility.  The result of `formatv` is an object which can be streamed to
13// a raw_ostream or converted to a std::string or llvm::SmallString.
14//
15//   // Convert to std::string.
16//   std::string S = formatv("{0} {1}", 1234.412, "test").str();
17//
18//   // Convert to llvm::SmallString
19//   SmallString<8> S = formatv("{0} {1}", 1234.412, "test").sstr<8>();
20//
21//   // Stream to an existing raw_ostream.
22//   OS << formatv("{0} {1}", 1234.412, "test");
23//
24//===----------------------------------------------------------------------===//
25
26#ifndef LLVM_SUPPORT_FORMATVARIADIC_H
27#define LLVM_SUPPORT_FORMATVARIADIC_H
28
29#include "llvm/ADT/Optional.h"
30#include "llvm/ADT/STLExtras.h"
31#include "llvm/ADT/SmallString.h"
32#include "llvm/ADT/StringRef.h"
33#include "llvm/Support/FormatCommon.h"
34#include "llvm/Support/FormatProviders.h"
35#include "llvm/Support/FormatVariadicDetails.h"
36#include "llvm/Support/raw_ostream.h"
37#include <cstddef>
38#include <string>
39#include <tuple>
40#include <utility>
41#include <vector>
42
43namespace llvm {
44
45enum class ReplacementType { Empty, Format, Literal };
46
47struct ReplacementItem {
48  ReplacementItem() = default;
49  explicit ReplacementItem(StringRef Literal)
50      : Type(ReplacementType::Literal), Spec(Literal) {}
51  ReplacementItem(StringRef Spec, size_t Index, size_t Align, AlignStyle Where,
52                  char Pad, StringRef Options)
53      : Type(ReplacementType::Format), Spec(Spec), Index(Index), Align(Align),
54        Where(Where), Pad(Pad), Options(Options) {}
55
56  ReplacementType Type = ReplacementType::Empty;
57  StringRef Spec;
58  size_t Index = 0;
59  size_t Align = 0;
60  AlignStyle Where = AlignStyle::Right;
61  char Pad;
62  StringRef Options;
63};
64
65class formatv_object_base {
66protected:
67  // The parameters are stored in a std::tuple, which does not provide runtime
68  // indexing capabilities.  In order to enable runtime indexing, we use this
69  // structure to put the parameters into a std::vector.  Since the parameters
70  // are not all the same type, we use some type-erasure by wrapping the
71  // parameters in a template class that derives from a non-template superclass.
72  // Essentially, we are converting a std::tuple<Derived<Ts...>> to a
73  // std::vector<Base*>.
74  struct create_adapters {
75    template <typename... Ts>
76    std::vector<detail::format_adapter *> operator()(Ts &... Items) {
77      return std::vector<detail::format_adapter *>{&Items...};
78    }
79  };
80
81  StringRef Fmt;
82  std::vector<detail::format_adapter *> Adapters;
83  std::vector<ReplacementItem> Replacements;
84
85  static bool consumeFieldLayout(StringRef &Spec, AlignStyle &Where,
86                                 size_t &Align, char &Pad);
87
88  static std::pair<ReplacementItem, StringRef>
89  splitLiteralAndReplacement(StringRef Fmt);
90
91public:
92  formatv_object_base(StringRef Fmt, std::size_t ParamCount)
93      : Fmt(Fmt), Replacements(parseFormatString(Fmt)) {
94    Adapters.reserve(ParamCount);
95  }
96
97  void format(raw_ostream &S) const {
98    for (auto &R : Replacements) {
99      if (R.Type == ReplacementType::Empty)
100        continue;
101      if (R.Type == ReplacementType::Literal) {
102        S << R.Spec;
103        continue;
104      }
105      if (R.Index >= Adapters.size()) {
106        S << R.Spec;
107        continue;
108      }
109
110      auto W = Adapters[R.Index];
111
112      FmtAlign Align(*W, R.Where, R.Align);
113      Align.format(S, R.Options);
114    }
115  }
116  static std::vector<ReplacementItem> parseFormatString(StringRef Fmt);
117
118  static Optional<ReplacementItem> parseReplacementItem(StringRef Spec);
119
120  std::string str() const {
121    std::string Result;
122    raw_string_ostream Stream(Result);
123    Stream << *this;
124    Stream.flush();
125    return Result;
126  }
127
128  template <unsigned N> SmallString<N> sstr() const {
129    SmallString<N> Result;
130    raw_svector_ostream Stream(Result);
131    Stream << *this;
132    return Result;
133  }
134
135  template <unsigned N> operator SmallString<N>() const { return sstr<N>(); }
136
137  operator std::string() const { return str(); }
138};
139
140template <typename Tuple> class formatv_object : public formatv_object_base {
141  // Storage for the parameter adapters.  Since the base class erases the type
142  // of the parameters, we have to own the storage for the parameters here, and
143  // have the base class store type-erased pointers into this tuple.
144  Tuple Parameters;
145
146public:
147  formatv_object(StringRef Fmt, Tuple &&Params)
148      : formatv_object_base(Fmt, std::tuple_size<Tuple>::value),
149        Parameters(std::move(Params)) {
150    Adapters = apply_tuple(create_adapters(), Parameters);
151  }
152};
153
154// \brief Format text given a format string and replacement parameters.
155//
156// ===General Description===
157//
158// Formats textual output.  `Fmt` is a string consisting of one or more
159// replacement sequences with the following grammar:
160//
161// rep_field ::= "{" [index] ["," layout] [":" format] "}"
162// index     ::= <non-negative integer>
163// layout    ::= [[[char]loc]width]
164// format    ::= <any string not containing "{" or "}">
165// char      ::= <any character except "{" or "}">
166// loc       ::= "-" | "=" | "+"
167// width     ::= <positive integer>
168//
169// index   - A non-negative integer specifying the index of the item in the
170//           parameter pack to print.  Any other value is invalid.
171// layout  - A string controlling how the field is laid out within the available
172//           space.
173// format  - A type-dependent string used to provide additional options to
174//           the formatting operation.  Refer to the documentation of the
175//           various individual format providers for per-type options.
176// char    - The padding character.  Defaults to ' ' (space).  Only valid if
177//           `loc` is also specified.
178// loc     - Where to print the formatted text within the field.  Only valid if
179//           `width` is also specified.
180//           '-' : The field is left aligned within the available space.
181//           '=' : The field is centered within the available space.
182//           '+' : The field is right aligned within the available space (this
183//                 is the default).
184// width   - The width of the field within which to print the formatted text.
185//           If this is less than the required length then the `char` and `loc`
186//           fields are ignored, and the field is printed with no leading or
187//           trailing padding.  If this is greater than the required length,
188//           then the text is output according to the value of `loc`, and padded
189//           as appropriate on the left and/or right by `char`.
190//
191// ===Special Characters===
192//
193// The characters '{' and '}' are reserved and cannot appear anywhere within a
194// replacement sequence.  Outside of a replacement sequence, in order to print
195// a literal '{' or '}' it must be doubled -- "{{" to print a literal '{' and
196// "}}" to print a literal '}'.
197//
198// ===Parameter Indexing===
199// `index` specifies the index of the parameter in the parameter pack to format
200// into the output.  Note that it is possible to refer to the same parameter
201// index multiple times in a given format string.  This makes it possible to
202// output the same value multiple times without passing it multiple times to the
203// function. For example:
204//
205//   formatv("{0} {1} {0}", "a", "bb")
206//
207// would yield the string "abba".  This can be convenient when it is expensive
208// to compute the value of the parameter, and you would otherwise have had to
209// save it to a temporary.
210//
211// ===Formatter Search===
212//
213// For a given parameter of type T, the following steps are executed in order
214// until a match is found:
215//
216//   1. If the parameter is of class type, and contains a method
217//      void format(raw_ostream &Stream, StringRef Options)
218//      Then this method is invoked to produce the formatted output.  The
219//      implementation should write the formatted text into `Stream`.
220//   2. If there is a suitable template specialization of format_provider<>
221//      for type T containing a method whose signature is:
222//      void format(const T &Obj, raw_ostream &Stream, StringRef Options)
223//      Then this method is invoked as described in Step 1.
224//
225// If a match cannot be found through either of the above methods, a compiler
226// error is generated.
227//
228// ===Invalid Format String Handling===
229//
230// In the case of a format string which does not match the grammar described
231// above, the output is undefined.  With asserts enabled, LLVM will trigger an
232// assertion.  Otherwise, it will try to do something reasonable, but in general
233// the details of what that is are undefined.
234//
235template <typename... Ts>
236inline auto formatv(const char *Fmt, Ts &&... Vals) -> formatv_object<decltype(
237    std::make_tuple(detail::build_format_adapter(std::forward<Ts>(Vals))...))> {
238  using ParamTuple = decltype(
239      std::make_tuple(detail::build_format_adapter(std::forward<Ts>(Vals))...));
240  return formatv_object<ParamTuple>(
241      Fmt,
242      std::make_tuple(detail::build_format_adapter(std::forward<Ts>(Vals))...));
243}
244
245} // end namespace llvm
246
247#endif // LLVM_SUPPORT_FORMATVARIADIC_H
248