1 //
2 // Copyright 2017 The Abseil Authors.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      https://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 // -----------------------------------------------------------------------------
17 // File: substitute.h
18 // -----------------------------------------------------------------------------
19 //
20 // This package contains functions for efficiently performing string
21 // substitutions using a format string with positional notation:
22 // `Substitute()` and `SubstituteAndAppend()`.
23 //
24 // Unlike printf-style format specifiers, `Substitute()` functions do not need
25 // to specify the type of the substitution arguments. Supported arguments
26 // following the format string, such as strings, string_views, ints,
27 // floats, and bools, are automatically converted to strings during the
28 // substitution process. (See below for a full list of supported types.)
29 //
30 // `Substitute()` does not allow you to specify *how* to format a value, beyond
31 // the default conversion to string. For example, you cannot format an integer
32 // in hex.
33 //
34 // The format string uses positional identifiers indicated by a dollar sign ($)
35 // and single digit positional ids to indicate which substitution arguments to
36 // use at that location within the format string.
37 //
38 // A '$$' sequence in the format string causes a literal '$' character to be
39 // output.
40 //
41 // Example 1:
42 //   std::string s = Substitute("$1 purchased $0 $2 for $$10. Thanks $1!",
43 //                              5, "Bob", "Apples");
44 //   EXPECT_EQ("Bob purchased 5 Apples for $10. Thanks Bob!", s);
45 //
46 // Example 2:
47 //   std::string s = "Hi. ";
48 //   SubstituteAndAppend(&s, "My name is $0 and I am $1 years old.", "Bob", 5);
49 //   EXPECT_EQ("Hi. My name is Bob and I am 5 years old.", s);
50 //
51 // Supported types:
52 //   * absl::string_view, std::string, const char* (null is equivalent to "")
53 //   * int32_t, int64_t, uint32_t, uint64
54 //   * float, double
55 //   * bool (Printed as "true" or "false")
56 //   * pointer types other than char* (Printed as "0x<lower case hex string>",
57 //     except that null is printed as "NULL")
58 //
59 // If an invalid format string is provided, Substitute returns an empty string
60 // and SubstituteAndAppend does not change the provided output string.
61 // A format string is invalid if it:
62 //   * ends in an unescaped $ character,
63 //     e.g. "Hello $", or
64 //   * calls for a position argument which is not provided,
65 //     e.g. Substitute("Hello $2", "world"), or
66 //   * specifies a non-digit, non-$ character after an unescaped $ character,
67 //     e.g. "Hello $f".
68 // In debug mode, i.e. #ifndef NDEBUG, such errors terminate the program.
69 
70 #ifndef ABSL_STRINGS_SUBSTITUTE_H_
71 #define ABSL_STRINGS_SUBSTITUTE_H_
72 
73 #include <cstring>
74 #include <string>
75 #include <type_traits>
76 #include <vector>
77 
78 #include "absl/base/macros.h"
79 #include "absl/base/port.h"
80 #include "absl/strings/ascii.h"
81 #include "absl/strings/escaping.h"
82 #include "absl/strings/numbers.h"
83 #include "absl/strings/str_cat.h"
84 #include "absl/strings/str_split.h"
85 #include "absl/strings/string_view.h"
86 #include "absl/strings/strip.h"
87 
88 namespace absl {
89 ABSL_NAMESPACE_BEGIN
90 namespace substitute_internal {
91 
92 // Arg
93 //
94 // This class provides an argument type for `absl::Substitute()` and
95 // `absl::SubstituteAndAppend()`. `Arg` handles implicit conversion of various
96 // types to a string. (`Arg` is very similar to the `AlphaNum` class in
97 // `StrCat()`.)
98 //
99 // This class has implicit constructors.
100 class Arg {
101  public:
102   // Overloads for string-y things
103   //
104   // Explicitly overload `const char*` so the compiler doesn't cast to `bool`.
Arg(const char * value)105   Arg(const char* value)  // NOLINT(runtime/explicit)
106       : piece_(absl::NullSafeStringView(value)) {}
107   template <typename Allocator>
Arg(const std::basic_string<char,std::char_traits<char>,Allocator> & value)108   Arg(  // NOLINT
109       const std::basic_string<char, std::char_traits<char>, Allocator>&
110           value) noexcept
111       : piece_(value) {}
Arg(absl::string_view value)112   Arg(absl::string_view value)  // NOLINT(runtime/explicit)
113       : piece_(value) {}
114 
115   // Overloads for primitives
116   //
117   // No overloads are available for signed and unsigned char because if people
118   // are explicitly declaring their chars as signed or unsigned then they are
119   // probably using them as 8-bit integers and would probably prefer an integer
120   // representation. However, we can't really know, so we make the caller decide
121   // what to do.
Arg(char value)122   Arg(char value)  // NOLINT(runtime/explicit)
123       : piece_(scratch_, 1) { scratch_[0] = value; }
Arg(short value)124   Arg(short value)  // NOLINT(*)
125       : piece_(scratch_,
126                numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {}
Arg(unsigned short value)127   Arg(unsigned short value)  // NOLINT(*)
128       : piece_(scratch_,
129                numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {}
Arg(int value)130   Arg(int value)  // NOLINT(runtime/explicit)
131       : piece_(scratch_,
132                numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {}
Arg(unsigned int value)133   Arg(unsigned int value)  // NOLINT(runtime/explicit)
134       : piece_(scratch_,
135                numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {}
Arg(long value)136   Arg(long value)  // NOLINT(*)
137       : piece_(scratch_,
138                numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {}
Arg(unsigned long value)139   Arg(unsigned long value)  // NOLINT(*)
140       : piece_(scratch_,
141                numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {}
Arg(long long value)142   Arg(long long value)  // NOLINT(*)
143       : piece_(scratch_,
144                numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {}
Arg(unsigned long long value)145   Arg(unsigned long long value)  // NOLINT(*)
146       : piece_(scratch_,
147                numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {}
Arg(float value)148   Arg(float value)  // NOLINT(runtime/explicit)
149       : piece_(scratch_, numbers_internal::SixDigitsToBuffer(value, scratch_)) {
150   }
Arg(double value)151   Arg(double value)  // NOLINT(runtime/explicit)
152       : piece_(scratch_, numbers_internal::SixDigitsToBuffer(value, scratch_)) {
153   }
Arg(bool value)154   Arg(bool value)  // NOLINT(runtime/explicit)
155       : piece_(value ? "true" : "false") {}
156 
157   Arg(Hex hex);  // NOLINT(runtime/explicit)
158   Arg(Dec dec);  // NOLINT(runtime/explicit)
159 
160   // vector<bool>::reference and const_reference require special help to
161   // convert to `AlphaNum` because it requires two user defined conversions.
162   template <typename T,
163             absl::enable_if_t<
164                 std::is_class<T>::value &&
165                 (std::is_same<T, std::vector<bool>::reference>::value ||
166                  std::is_same<T, std::vector<bool>::const_reference>::value)>* =
167                 nullptr>
Arg(T value)168   Arg(T value)  // NOLINT(google-explicit-constructor)
169       : Arg(static_cast<bool>(value)) {}
170 
171   // `void*` values, with the exception of `char*`, are printed as
172   // "0x<hex value>". However, in the case of `nullptr`, "NULL" is printed.
173   Arg(const void* value);  // NOLINT(runtime/explicit)
174 
175   Arg(const Arg&) = delete;
176   Arg& operator=(const Arg&) = delete;
177 
piece()178   absl::string_view piece() const { return piece_; }
179 
180  private:
181   absl::string_view piece_;
182   char scratch_[numbers_internal::kFastToBufferSize];
183 };
184 
185 // Internal helper function. Don't call this from outside this implementation.
186 // This interface may change without notice.
187 void SubstituteAndAppendArray(std::string* output, absl::string_view format,
188                               const absl::string_view* args_array,
189                               size_t num_args);
190 
191 #if defined(ABSL_BAD_CALL_IF)
CalculateOneBit(const char * format)192 constexpr int CalculateOneBit(const char* format) {
193   // Returns:
194   // * 2^N for '$N' when N is in [0-9]
195   // * 0 for correct '$' escaping: '$$'.
196   // * -1 otherwise.
197   return (*format < '0' || *format > '9') ? (*format == '$' ? 0 : -1)
198                                           : (1 << (*format - '0'));
199 }
200 
SkipNumber(const char * format)201 constexpr const char* SkipNumber(const char* format) {
202   return !*format ? format : (format + 1);
203 }
204 
PlaceholderBitmask(const char * format)205 constexpr int PlaceholderBitmask(const char* format) {
206   return !*format ? 0 : *format != '$'
207                              ? PlaceholderBitmask(format + 1)
208                              : (CalculateOneBit(format + 1) |
209                                    PlaceholderBitmask(SkipNumber(format + 1)));
210 }
211 #endif  // ABSL_BAD_CALL_IF
212 
213 }  // namespace substitute_internal
214 
215 //
216 // PUBLIC API
217 //
218 
219 // SubstituteAndAppend()
220 //
221 // Substitutes variables into a given format string and appends to a given
222 // output string. See file comments above for usage.
223 //
224 // The declarations of `SubstituteAndAppend()` below consist of overloads
225 // for passing 0 to 10 arguments, respectively.
226 //
227 // NOTE: A zero-argument `SubstituteAndAppend()` may be used within variadic
228 // templates to allow a variable number of arguments.
229 //
230 // Example:
231 //  template <typename... Args>
232 //  void VarMsg(std::string* boilerplate, absl::string_view format,
233 //      const Args&... args) {
234 //    absl::SubstituteAndAppend(boilerplate, format, args...);
235 //  }
236 //
SubstituteAndAppend(std::string * output,absl::string_view format)237 inline void SubstituteAndAppend(std::string* output, absl::string_view format) {
238   substitute_internal::SubstituteAndAppendArray(output, format, nullptr, 0);
239 }
240 
SubstituteAndAppend(std::string * output,absl::string_view format,const substitute_internal::Arg & a0)241 inline void SubstituteAndAppend(std::string* output, absl::string_view format,
242                                 const substitute_internal::Arg& a0) {
243   const absl::string_view args[] = {a0.piece()};
244   substitute_internal::SubstituteAndAppendArray(output, format, args,
245                                                 ABSL_ARRAYSIZE(args));
246 }
247 
SubstituteAndAppend(std::string * output,absl::string_view format,const substitute_internal::Arg & a0,const substitute_internal::Arg & a1)248 inline void SubstituteAndAppend(std::string* output, absl::string_view format,
249                                 const substitute_internal::Arg& a0,
250                                 const substitute_internal::Arg& a1) {
251   const absl::string_view args[] = {a0.piece(), a1.piece()};
252   substitute_internal::SubstituteAndAppendArray(output, format, args,
253                                                 ABSL_ARRAYSIZE(args));
254 }
255 
SubstituteAndAppend(std::string * output,absl::string_view format,const substitute_internal::Arg & a0,const substitute_internal::Arg & a1,const substitute_internal::Arg & a2)256 inline void SubstituteAndAppend(std::string* output, absl::string_view format,
257                                 const substitute_internal::Arg& a0,
258                                 const substitute_internal::Arg& a1,
259                                 const substitute_internal::Arg& a2) {
260   const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece()};
261   substitute_internal::SubstituteAndAppendArray(output, format, args,
262                                                 ABSL_ARRAYSIZE(args));
263 }
264 
SubstituteAndAppend(std::string * output,absl::string_view format,const substitute_internal::Arg & a0,const substitute_internal::Arg & a1,const substitute_internal::Arg & a2,const substitute_internal::Arg & a3)265 inline void SubstituteAndAppend(std::string* output, absl::string_view format,
266                                 const substitute_internal::Arg& a0,
267                                 const substitute_internal::Arg& a1,
268                                 const substitute_internal::Arg& a2,
269                                 const substitute_internal::Arg& a3) {
270   const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
271                                     a3.piece()};
272   substitute_internal::SubstituteAndAppendArray(output, format, args,
273                                                 ABSL_ARRAYSIZE(args));
274 }
275 
SubstituteAndAppend(std::string * output,absl::string_view format,const substitute_internal::Arg & a0,const substitute_internal::Arg & a1,const substitute_internal::Arg & a2,const substitute_internal::Arg & a3,const substitute_internal::Arg & a4)276 inline void SubstituteAndAppend(std::string* output, absl::string_view format,
277                                 const substitute_internal::Arg& a0,
278                                 const substitute_internal::Arg& a1,
279                                 const substitute_internal::Arg& a2,
280                                 const substitute_internal::Arg& a3,
281                                 const substitute_internal::Arg& a4) {
282   const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
283                                     a3.piece(), a4.piece()};
284   substitute_internal::SubstituteAndAppendArray(output, format, args,
285                                                 ABSL_ARRAYSIZE(args));
286 }
287 
SubstituteAndAppend(std::string * output,absl::string_view format,const substitute_internal::Arg & a0,const substitute_internal::Arg & a1,const substitute_internal::Arg & a2,const substitute_internal::Arg & a3,const substitute_internal::Arg & a4,const substitute_internal::Arg & a5)288 inline void SubstituteAndAppend(std::string* output, absl::string_view format,
289                                 const substitute_internal::Arg& a0,
290                                 const substitute_internal::Arg& a1,
291                                 const substitute_internal::Arg& a2,
292                                 const substitute_internal::Arg& a3,
293                                 const substitute_internal::Arg& a4,
294                                 const substitute_internal::Arg& a5) {
295   const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
296                                     a3.piece(), a4.piece(), a5.piece()};
297   substitute_internal::SubstituteAndAppendArray(output, format, args,
298                                                 ABSL_ARRAYSIZE(args));
299 }
300 
SubstituteAndAppend(std::string * output,absl::string_view format,const substitute_internal::Arg & a0,const substitute_internal::Arg & a1,const substitute_internal::Arg & a2,const substitute_internal::Arg & a3,const substitute_internal::Arg & a4,const substitute_internal::Arg & a5,const substitute_internal::Arg & a6)301 inline void SubstituteAndAppend(std::string* output, absl::string_view format,
302                                 const substitute_internal::Arg& a0,
303                                 const substitute_internal::Arg& a1,
304                                 const substitute_internal::Arg& a2,
305                                 const substitute_internal::Arg& a3,
306                                 const substitute_internal::Arg& a4,
307                                 const substitute_internal::Arg& a5,
308                                 const substitute_internal::Arg& a6) {
309   const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
310                                     a3.piece(), a4.piece(), a5.piece(),
311                                     a6.piece()};
312   substitute_internal::SubstituteAndAppendArray(output, format, args,
313                                                 ABSL_ARRAYSIZE(args));
314 }
315 
SubstituteAndAppend(std::string * output,absl::string_view format,const substitute_internal::Arg & a0,const substitute_internal::Arg & a1,const substitute_internal::Arg & a2,const substitute_internal::Arg & a3,const substitute_internal::Arg & a4,const substitute_internal::Arg & a5,const substitute_internal::Arg & a6,const substitute_internal::Arg & a7)316 inline void SubstituteAndAppend(
317     std::string* output, absl::string_view format,
318     const substitute_internal::Arg& a0, const substitute_internal::Arg& a1,
319     const substitute_internal::Arg& a2, const substitute_internal::Arg& a3,
320     const substitute_internal::Arg& a4, const substitute_internal::Arg& a5,
321     const substitute_internal::Arg& a6, const substitute_internal::Arg& a7) {
322   const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
323                                     a3.piece(), a4.piece(), a5.piece(),
324                                     a6.piece(), a7.piece()};
325   substitute_internal::SubstituteAndAppendArray(output, format, args,
326                                                 ABSL_ARRAYSIZE(args));
327 }
328 
SubstituteAndAppend(std::string * output,absl::string_view format,const substitute_internal::Arg & a0,const substitute_internal::Arg & a1,const substitute_internal::Arg & a2,const substitute_internal::Arg & a3,const substitute_internal::Arg & a4,const substitute_internal::Arg & a5,const substitute_internal::Arg & a6,const substitute_internal::Arg & a7,const substitute_internal::Arg & a8)329 inline void SubstituteAndAppend(
330     std::string* output, absl::string_view format,
331     const substitute_internal::Arg& a0, const substitute_internal::Arg& a1,
332     const substitute_internal::Arg& a2, const substitute_internal::Arg& a3,
333     const substitute_internal::Arg& a4, const substitute_internal::Arg& a5,
334     const substitute_internal::Arg& a6, const substitute_internal::Arg& a7,
335     const substitute_internal::Arg& a8) {
336   const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
337                                     a3.piece(), a4.piece(), a5.piece(),
338                                     a6.piece(), a7.piece(), a8.piece()};
339   substitute_internal::SubstituteAndAppendArray(output, format, args,
340                                                 ABSL_ARRAYSIZE(args));
341 }
342 
SubstituteAndAppend(std::string * output,absl::string_view format,const substitute_internal::Arg & a0,const substitute_internal::Arg & a1,const substitute_internal::Arg & a2,const substitute_internal::Arg & a3,const substitute_internal::Arg & a4,const substitute_internal::Arg & a5,const substitute_internal::Arg & a6,const substitute_internal::Arg & a7,const substitute_internal::Arg & a8,const substitute_internal::Arg & a9)343 inline void SubstituteAndAppend(
344     std::string* output, absl::string_view format,
345     const substitute_internal::Arg& a0, const substitute_internal::Arg& a1,
346     const substitute_internal::Arg& a2, const substitute_internal::Arg& a3,
347     const substitute_internal::Arg& a4, const substitute_internal::Arg& a5,
348     const substitute_internal::Arg& a6, const substitute_internal::Arg& a7,
349     const substitute_internal::Arg& a8, const substitute_internal::Arg& a9) {
350   const absl::string_view args[] = {
351       a0.piece(), a1.piece(), a2.piece(), a3.piece(), a4.piece(),
352       a5.piece(), a6.piece(), a7.piece(), a8.piece(), a9.piece()};
353   substitute_internal::SubstituteAndAppendArray(output, format, args,
354                                                 ABSL_ARRAYSIZE(args));
355 }
356 
357 #if defined(ABSL_BAD_CALL_IF)
358 // This body of functions catches cases where the number of placeholders
359 // doesn't match the number of data arguments.
360 void SubstituteAndAppend(std::string* output, const char* format)
361     ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 0,
362                      "There were no substitution arguments "
363                      "but this format string has a $[0-9] in it");
364 
365 void SubstituteAndAppend(std::string* output, const char* format,
366                          const substitute_internal::Arg& a0)
367     ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 1,
368                      "There was 1 substitution argument given, but "
369                      "this format string is either missing its $0, or "
370                      "contains one of $1-$9");
371 
372 void SubstituteAndAppend(std::string* output, const char* format,
373                          const substitute_internal::Arg& a0,
374                          const substitute_internal::Arg& a1)
375     ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 3,
376                      "There were 2 substitution arguments given, but "
377                      "this format string is either missing its $0/$1, or "
378                      "contains one of $2-$9");
379 
380 void SubstituteAndAppend(std::string* output, const char* format,
381                          const substitute_internal::Arg& a0,
382                          const substitute_internal::Arg& a1,
383                          const substitute_internal::Arg& a2)
384     ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 7,
385                      "There were 3 substitution arguments given, but "
386                      "this format string is either missing its $0/$1/$2, or "
387                      "contains one of $3-$9");
388 
389 void SubstituteAndAppend(std::string* output, const char* format,
390                          const substitute_internal::Arg& a0,
391                          const substitute_internal::Arg& a1,
392                          const substitute_internal::Arg& a2,
393                          const substitute_internal::Arg& a3)
394     ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 15,
395                      "There were 4 substitution arguments given, but "
396                      "this format string is either missing its $0-$3, or "
397                      "contains one of $4-$9");
398 
399 void SubstituteAndAppend(std::string* output, const char* format,
400                          const substitute_internal::Arg& a0,
401                          const substitute_internal::Arg& a1,
402                          const substitute_internal::Arg& a2,
403                          const substitute_internal::Arg& a3,
404                          const substitute_internal::Arg& a4)
405     ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 31,
406                      "There were 5 substitution arguments given, but "
407                      "this format string is either missing its $0-$4, or "
408                      "contains one of $5-$9");
409 
410 void SubstituteAndAppend(std::string* output, const char* format,
411                          const substitute_internal::Arg& a0,
412                          const substitute_internal::Arg& a1,
413                          const substitute_internal::Arg& a2,
414                          const substitute_internal::Arg& a3,
415                          const substitute_internal::Arg& a4,
416                          const substitute_internal::Arg& a5)
417     ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 63,
418                      "There were 6 substitution arguments given, but "
419                      "this format string is either missing its $0-$5, or "
420                      "contains one of $6-$9");
421 
422 void SubstituteAndAppend(
423     std::string* output, const char* format, const substitute_internal::Arg& a0,
424     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
425     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
426     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6)
427     ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 127,
428                      "There were 7 substitution arguments given, but "
429                      "this format string is either missing its $0-$6, or "
430                      "contains one of $7-$9");
431 
432 void SubstituteAndAppend(
433     std::string* output, const char* format, const substitute_internal::Arg& a0,
434     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
435     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
436     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
437     const substitute_internal::Arg& a7)
438     ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 255,
439                      "There were 8 substitution arguments given, but "
440                      "this format string is either missing its $0-$7, or "
441                      "contains one of $8-$9");
442 
443 void SubstituteAndAppend(
444     std::string* output, const char* format, const substitute_internal::Arg& a0,
445     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
446     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
447     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
448     const substitute_internal::Arg& a7, const substitute_internal::Arg& a8)
449     ABSL_BAD_CALL_IF(
450         substitute_internal::PlaceholderBitmask(format) != 511,
451         "There were 9 substitution arguments given, but "
452         "this format string is either missing its $0-$8, or contains a $9");
453 
454 void SubstituteAndAppend(
455     std::string* output, const char* format, const substitute_internal::Arg& a0,
456     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
457     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
458     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
459     const substitute_internal::Arg& a7, const substitute_internal::Arg& a8,
460     const substitute_internal::Arg& a9)
461     ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 1023,
462                      "There were 10 substitution arguments given, but this "
463                      "format string doesn't contain all of $0 through $9");
464 #endif  // ABSL_BAD_CALL_IF
465 
466 // Substitute()
467 //
468 // Substitutes variables into a given format string. See file comments above
469 // for usage.
470 //
471 // The declarations of `Substitute()` below consist of overloads for passing 0
472 // to 10 arguments, respectively.
473 //
474 // NOTE: A zero-argument `Substitute()` may be used within variadic templates to
475 // allow a variable number of arguments.
476 //
477 // Example:
478 //  template <typename... Args>
479 //  void VarMsg(absl::string_view format, const Args&... args) {
480 //    std::string s = absl::Substitute(format, args...);
481 
Substitute(absl::string_view format)482 ABSL_MUST_USE_RESULT inline std::string Substitute(absl::string_view format) {
483   std::string result;
484   SubstituteAndAppend(&result, format);
485   return result;
486 }
487 
Substitute(absl::string_view format,const substitute_internal::Arg & a0)488 ABSL_MUST_USE_RESULT inline std::string Substitute(
489     absl::string_view format, const substitute_internal::Arg& a0) {
490   std::string result;
491   SubstituteAndAppend(&result, format, a0);
492   return result;
493 }
494 
Substitute(absl::string_view format,const substitute_internal::Arg & a0,const substitute_internal::Arg & a1)495 ABSL_MUST_USE_RESULT inline std::string Substitute(
496     absl::string_view format, const substitute_internal::Arg& a0,
497     const substitute_internal::Arg& a1) {
498   std::string result;
499   SubstituteAndAppend(&result, format, a0, a1);
500   return result;
501 }
502 
Substitute(absl::string_view format,const substitute_internal::Arg & a0,const substitute_internal::Arg & a1,const substitute_internal::Arg & a2)503 ABSL_MUST_USE_RESULT inline std::string Substitute(
504     absl::string_view format, const substitute_internal::Arg& a0,
505     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2) {
506   std::string result;
507   SubstituteAndAppend(&result, format, a0, a1, a2);
508   return result;
509 }
510 
Substitute(absl::string_view format,const substitute_internal::Arg & a0,const substitute_internal::Arg & a1,const substitute_internal::Arg & a2,const substitute_internal::Arg & a3)511 ABSL_MUST_USE_RESULT inline std::string Substitute(
512     absl::string_view format, const substitute_internal::Arg& a0,
513     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
514     const substitute_internal::Arg& a3) {
515   std::string result;
516   SubstituteAndAppend(&result, format, a0, a1, a2, a3);
517   return result;
518 }
519 
Substitute(absl::string_view format,const substitute_internal::Arg & a0,const substitute_internal::Arg & a1,const substitute_internal::Arg & a2,const substitute_internal::Arg & a3,const substitute_internal::Arg & a4)520 ABSL_MUST_USE_RESULT inline std::string Substitute(
521     absl::string_view format, const substitute_internal::Arg& a0,
522     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
523     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4) {
524   std::string result;
525   SubstituteAndAppend(&result, format, a0, a1, a2, a3, a4);
526   return result;
527 }
528 
Substitute(absl::string_view format,const substitute_internal::Arg & a0,const substitute_internal::Arg & a1,const substitute_internal::Arg & a2,const substitute_internal::Arg & a3,const substitute_internal::Arg & a4,const substitute_internal::Arg & a5)529 ABSL_MUST_USE_RESULT inline std::string Substitute(
530     absl::string_view format, const substitute_internal::Arg& a0,
531     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
532     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
533     const substitute_internal::Arg& a5) {
534   std::string result;
535   SubstituteAndAppend(&result, format, a0, a1, a2, a3, a4, a5);
536   return result;
537 }
538 
Substitute(absl::string_view format,const substitute_internal::Arg & a0,const substitute_internal::Arg & a1,const substitute_internal::Arg & a2,const substitute_internal::Arg & a3,const substitute_internal::Arg & a4,const substitute_internal::Arg & a5,const substitute_internal::Arg & a6)539 ABSL_MUST_USE_RESULT inline std::string Substitute(
540     absl::string_view format, const substitute_internal::Arg& a0,
541     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
542     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
543     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6) {
544   std::string result;
545   SubstituteAndAppend(&result, format, a0, a1, a2, a3, a4, a5, a6);
546   return result;
547 }
548 
Substitute(absl::string_view format,const substitute_internal::Arg & a0,const substitute_internal::Arg & a1,const substitute_internal::Arg & a2,const substitute_internal::Arg & a3,const substitute_internal::Arg & a4,const substitute_internal::Arg & a5,const substitute_internal::Arg & a6,const substitute_internal::Arg & a7)549 ABSL_MUST_USE_RESULT inline std::string Substitute(
550     absl::string_view format, const substitute_internal::Arg& a0,
551     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
552     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
553     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
554     const substitute_internal::Arg& a7) {
555   std::string result;
556   SubstituteAndAppend(&result, format, a0, a1, a2, a3, a4, a5, a6, a7);
557   return result;
558 }
559 
Substitute(absl::string_view format,const substitute_internal::Arg & a0,const substitute_internal::Arg & a1,const substitute_internal::Arg & a2,const substitute_internal::Arg & a3,const substitute_internal::Arg & a4,const substitute_internal::Arg & a5,const substitute_internal::Arg & a6,const substitute_internal::Arg & a7,const substitute_internal::Arg & a8)560 ABSL_MUST_USE_RESULT inline std::string Substitute(
561     absl::string_view format, const substitute_internal::Arg& a0,
562     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
563     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
564     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
565     const substitute_internal::Arg& a7, const substitute_internal::Arg& a8) {
566   std::string result;
567   SubstituteAndAppend(&result, format, a0, a1, a2, a3, a4, a5, a6, a7, a8);
568   return result;
569 }
570 
Substitute(absl::string_view format,const substitute_internal::Arg & a0,const substitute_internal::Arg & a1,const substitute_internal::Arg & a2,const substitute_internal::Arg & a3,const substitute_internal::Arg & a4,const substitute_internal::Arg & a5,const substitute_internal::Arg & a6,const substitute_internal::Arg & a7,const substitute_internal::Arg & a8,const substitute_internal::Arg & a9)571 ABSL_MUST_USE_RESULT inline std::string Substitute(
572     absl::string_view format, const substitute_internal::Arg& a0,
573     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
574     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
575     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
576     const substitute_internal::Arg& a7, const substitute_internal::Arg& a8,
577     const substitute_internal::Arg& a9) {
578   std::string result;
579   SubstituteAndAppend(&result, format, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
580   return result;
581 }
582 
583 #if defined(ABSL_BAD_CALL_IF)
584 // This body of functions catches cases where the number of placeholders
585 // doesn't match the number of data arguments.
586 std::string Substitute(const char* format)
587     ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 0,
588                      "There were no substitution arguments "
589                      "but this format string has a $[0-9] in it");
590 
591 std::string Substitute(const char* format, const substitute_internal::Arg& a0)
592     ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 1,
593                      "There was 1 substitution argument given, but "
594                      "this format string is either missing its $0, or "
595                      "contains one of $1-$9");
596 
597 std::string Substitute(const char* format, const substitute_internal::Arg& a0,
598                        const substitute_internal::Arg& a1)
599     ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 3,
600                      "There were 2 substitution arguments given, but "
601                      "this format string is either missing its $0/$1, or "
602                      "contains one of $2-$9");
603 
604 std::string Substitute(const char* format, const substitute_internal::Arg& a0,
605                        const substitute_internal::Arg& a1,
606                        const substitute_internal::Arg& a2)
607     ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 7,
608                      "There were 3 substitution arguments given, but "
609                      "this format string is either missing its $0/$1/$2, or "
610                      "contains one of $3-$9");
611 
612 std::string Substitute(const char* format, const substitute_internal::Arg& a0,
613                        const substitute_internal::Arg& a1,
614                        const substitute_internal::Arg& a2,
615                        const substitute_internal::Arg& a3)
616     ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 15,
617                      "There were 4 substitution arguments given, but "
618                      "this format string is either missing its $0-$3, or "
619                      "contains one of $4-$9");
620 
621 std::string Substitute(const char* format, const substitute_internal::Arg& a0,
622                        const substitute_internal::Arg& a1,
623                        const substitute_internal::Arg& a2,
624                        const substitute_internal::Arg& a3,
625                        const substitute_internal::Arg& a4)
626     ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 31,
627                      "There were 5 substitution arguments given, but "
628                      "this format string is either missing its $0-$4, or "
629                      "contains one of $5-$9");
630 
631 std::string Substitute(const char* format, const substitute_internal::Arg& a0,
632                        const substitute_internal::Arg& a1,
633                        const substitute_internal::Arg& a2,
634                        const substitute_internal::Arg& a3,
635                        const substitute_internal::Arg& a4,
636                        const substitute_internal::Arg& a5)
637     ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 63,
638                      "There were 6 substitution arguments given, but "
639                      "this format string is either missing its $0-$5, or "
640                      "contains one of $6-$9");
641 
642 std::string Substitute(const char* format, const substitute_internal::Arg& a0,
643                        const substitute_internal::Arg& a1,
644                        const substitute_internal::Arg& a2,
645                        const substitute_internal::Arg& a3,
646                        const substitute_internal::Arg& a4,
647                        const substitute_internal::Arg& a5,
648                        const substitute_internal::Arg& a6)
649     ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 127,
650                      "There were 7 substitution arguments given, but "
651                      "this format string is either missing its $0-$6, or "
652                      "contains one of $7-$9");
653 
654 std::string Substitute(const char* format, const substitute_internal::Arg& a0,
655                        const substitute_internal::Arg& a1,
656                        const substitute_internal::Arg& a2,
657                        const substitute_internal::Arg& a3,
658                        const substitute_internal::Arg& a4,
659                        const substitute_internal::Arg& a5,
660                        const substitute_internal::Arg& a6,
661                        const substitute_internal::Arg& a7)
662     ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 255,
663                      "There were 8 substitution arguments given, but "
664                      "this format string is either missing its $0-$7, or "
665                      "contains one of $8-$9");
666 
667 std::string Substitute(
668     const char* format, const substitute_internal::Arg& a0,
669     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
670     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
671     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
672     const substitute_internal::Arg& a7, const substitute_internal::Arg& a8)
673     ABSL_BAD_CALL_IF(
674         substitute_internal::PlaceholderBitmask(format) != 511,
675         "There were 9 substitution arguments given, but "
676         "this format string is either missing its $0-$8, or contains a $9");
677 
678 std::string Substitute(
679     const char* format, const substitute_internal::Arg& a0,
680     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
681     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
682     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
683     const substitute_internal::Arg& a7, const substitute_internal::Arg& a8,
684     const substitute_internal::Arg& a9)
685     ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 1023,
686                      "There were 10 substitution arguments given, but this "
687                      "format string doesn't contain all of $0 through $9");
688 #endif  // ABSL_BAD_CALL_IF
689 
690 ABSL_NAMESPACE_END
691 }  // namespace absl
692 
693 #endif  // ABSL_STRINGS_SUBSTITUTE_H_
694