• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_t
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) {
124     scratch_[0] = value;
125   }
Arg(short value)126   Arg(short value)  // NOLINT(*)
127       : piece_(scratch_,
128                numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {}
Arg(unsigned short value)129   Arg(unsigned short value)  // NOLINT(*)
130       : piece_(scratch_,
131                numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {}
Arg(int value)132   Arg(int value)  // NOLINT(runtime/explicit)
133       : piece_(scratch_,
134                numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {}
Arg(unsigned int value)135   Arg(unsigned int value)  // NOLINT(runtime/explicit)
136       : piece_(scratch_,
137                numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {}
Arg(long value)138   Arg(long value)  // NOLINT(*)
139       : piece_(scratch_,
140                numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {}
Arg(unsigned long value)141   Arg(unsigned long value)  // NOLINT(*)
142       : piece_(scratch_,
143                numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {}
Arg(long long value)144   Arg(long long value)  // NOLINT(*)
145       : piece_(scratch_,
146                numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {}
Arg(unsigned long long value)147   Arg(unsigned long long value)  // NOLINT(*)
148       : piece_(scratch_,
149                numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {}
Arg(float value)150   Arg(float value)  // NOLINT(runtime/explicit)
151       : piece_(scratch_, numbers_internal::SixDigitsToBuffer(value, scratch_)) {
152   }
Arg(double value)153   Arg(double value)  // NOLINT(runtime/explicit)
154       : piece_(scratch_, numbers_internal::SixDigitsToBuffer(value, scratch_)) {
155   }
Arg(bool value)156   Arg(bool value)  // NOLINT(runtime/explicit)
157       : piece_(value ? "true" : "false") {}
158 
159   Arg(Hex hex);  // NOLINT(runtime/explicit)
160   Arg(Dec dec);  // NOLINT(runtime/explicit)
161 
162   // vector<bool>::reference and const_reference require special help to convert
163   // to `Arg` because it requires two user defined conversions.
164   template <typename T,
165             absl::enable_if_t<
166                 std::is_class<T>::value &&
167                 (std::is_same<T, std::vector<bool>::reference>::value ||
168                  std::is_same<T, std::vector<bool>::const_reference>::value)>* =
169                 nullptr>
Arg(T value)170   Arg(T value)  // NOLINT(google-explicit-constructor)
171       : Arg(static_cast<bool>(value)) {}
172 
173   // `void*` values, with the exception of `char*`, are printed as
174   // "0x<hex value>". However, in the case of `nullptr`, "NULL" is printed.
175   Arg(const void* value);  // NOLINT(runtime/explicit)
176 
177   // Normal enums are already handled by the integer formatters.
178   // This overload matches only scoped enums.
179   template <typename T,
180             typename = typename std::enable_if<
181                 std::is_enum<T>{} && !std::is_convertible<T, int>{}>::type>
Arg(T value)182   Arg(T value)  // NOLINT(google-explicit-constructor)
183       : Arg(static_cast<typename std::underlying_type<T>::type>(value)) {}
184 
185   Arg(const Arg&) = delete;
186   Arg& operator=(const Arg&) = delete;
187 
piece()188   absl::string_view piece() const { return piece_; }
189 
190  private:
191   absl::string_view piece_;
192   char scratch_[numbers_internal::kFastToBufferSize];
193 };
194 
195 // Internal helper function. Don't call this from outside this implementation.
196 // This interface may change without notice.
197 void SubstituteAndAppendArray(std::string* output, absl::string_view format,
198                               const absl::string_view* args_array,
199                               size_t num_args);
200 
201 #if defined(ABSL_BAD_CALL_IF)
CalculateOneBit(const char * format)202 constexpr int CalculateOneBit(const char* format) {
203   // Returns:
204   // * 2^N for '$N' when N is in [0-9]
205   // * 0 for correct '$' escaping: '$$'.
206   // * -1 otherwise.
207   return (*format < '0' || *format > '9') ? (*format == '$' ? 0 : -1)
208                                           : (1 << (*format - '0'));
209 }
210 
SkipNumber(const char * format)211 constexpr const char* SkipNumber(const char* format) {
212   return !*format ? format : (format + 1);
213 }
214 
PlaceholderBitmask(const char * format)215 constexpr int PlaceholderBitmask(const char* format) {
216   return !*format
217              ? 0
218              : *format != '$' ? PlaceholderBitmask(format + 1)
219                               : (CalculateOneBit(format + 1) |
220                                  PlaceholderBitmask(SkipNumber(format + 1)));
221 }
222 #endif  // ABSL_BAD_CALL_IF
223 
224 }  // namespace substitute_internal
225 
226 //
227 // PUBLIC API
228 //
229 
230 // SubstituteAndAppend()
231 //
232 // Substitutes variables into a given format string and appends to a given
233 // output string. See file comments above for usage.
234 //
235 // The declarations of `SubstituteAndAppend()` below consist of overloads
236 // for passing 0 to 10 arguments, respectively.
237 //
238 // NOTE: A zero-argument `SubstituteAndAppend()` may be used within variadic
239 // templates to allow a variable number of arguments.
240 //
241 // Example:
242 //  template <typename... Args>
243 //  void VarMsg(std::string* boilerplate, absl::string_view format,
244 //      const Args&... args) {
245 //    absl::SubstituteAndAppend(boilerplate, format, args...);
246 //  }
247 //
SubstituteAndAppend(std::string * output,absl::string_view format)248 inline void SubstituteAndAppend(std::string* output, absl::string_view format) {
249   substitute_internal::SubstituteAndAppendArray(output, format, nullptr, 0);
250 }
251 
SubstituteAndAppend(std::string * output,absl::string_view format,const substitute_internal::Arg & a0)252 inline void SubstituteAndAppend(std::string* output, absl::string_view format,
253                                 const substitute_internal::Arg& a0) {
254   const absl::string_view args[] = {a0.piece()};
255   substitute_internal::SubstituteAndAppendArray(output, format, args,
256                                                 ABSL_ARRAYSIZE(args));
257 }
258 
SubstituteAndAppend(std::string * output,absl::string_view format,const substitute_internal::Arg & a0,const substitute_internal::Arg & a1)259 inline void SubstituteAndAppend(std::string* output, absl::string_view format,
260                                 const substitute_internal::Arg& a0,
261                                 const substitute_internal::Arg& a1) {
262   const absl::string_view args[] = {a0.piece(), a1.piece()};
263   substitute_internal::SubstituteAndAppendArray(output, format, args,
264                                                 ABSL_ARRAYSIZE(args));
265 }
266 
SubstituteAndAppend(std::string * output,absl::string_view format,const substitute_internal::Arg & a0,const substitute_internal::Arg & a1,const substitute_internal::Arg & a2)267 inline void SubstituteAndAppend(std::string* output, absl::string_view format,
268                                 const substitute_internal::Arg& a0,
269                                 const substitute_internal::Arg& a1,
270                                 const substitute_internal::Arg& a2) {
271   const absl::string_view args[] = {a0.piece(), a1.piece(), a2.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)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 absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
282                                     a3.piece()};
283   substitute_internal::SubstituteAndAppendArray(output, format, args,
284                                                 ABSL_ARRAYSIZE(args));
285 }
286 
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)287 inline void SubstituteAndAppend(std::string* output, absl::string_view format,
288                                 const substitute_internal::Arg& a0,
289                                 const substitute_internal::Arg& a1,
290                                 const substitute_internal::Arg& a2,
291                                 const substitute_internal::Arg& a3,
292                                 const substitute_internal::Arg& a4) {
293   const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
294                                     a3.piece(), a4.piece()};
295   substitute_internal::SubstituteAndAppendArray(output, format, args,
296                                                 ABSL_ARRAYSIZE(args));
297 }
298 
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)299 inline void SubstituteAndAppend(std::string* output, absl::string_view format,
300                                 const substitute_internal::Arg& a0,
301                                 const substitute_internal::Arg& a1,
302                                 const substitute_internal::Arg& a2,
303                                 const substitute_internal::Arg& a3,
304                                 const substitute_internal::Arg& a4,
305                                 const substitute_internal::Arg& a5) {
306   const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
307                                     a3.piece(), a4.piece(), a5.piece()};
308   substitute_internal::SubstituteAndAppendArray(output, format, args,
309                                                 ABSL_ARRAYSIZE(args));
310 }
311 
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)312 inline void SubstituteAndAppend(std::string* output, absl::string_view format,
313                                 const substitute_internal::Arg& a0,
314                                 const substitute_internal::Arg& a1,
315                                 const substitute_internal::Arg& a2,
316                                 const substitute_internal::Arg& a3,
317                                 const substitute_internal::Arg& a4,
318                                 const substitute_internal::Arg& a5,
319                                 const substitute_internal::Arg& a6) {
320   const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
321                                     a3.piece(), a4.piece(), a5.piece(),
322                                     a6.piece()};
323   substitute_internal::SubstituteAndAppendArray(output, format, args,
324                                                 ABSL_ARRAYSIZE(args));
325 }
326 
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)327 inline void SubstituteAndAppend(
328     std::string* output, absl::string_view format,
329     const substitute_internal::Arg& a0, const substitute_internal::Arg& a1,
330     const substitute_internal::Arg& a2, const substitute_internal::Arg& a3,
331     const substitute_internal::Arg& a4, const substitute_internal::Arg& a5,
332     const substitute_internal::Arg& a6, const substitute_internal::Arg& a7) {
333   const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
334                                     a3.piece(), a4.piece(), a5.piece(),
335                                     a6.piece(), a7.piece()};
336   substitute_internal::SubstituteAndAppendArray(output, format, args,
337                                                 ABSL_ARRAYSIZE(args));
338 }
339 
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)340 inline void SubstituteAndAppend(
341     std::string* output, absl::string_view format,
342     const substitute_internal::Arg& a0, const substitute_internal::Arg& a1,
343     const substitute_internal::Arg& a2, const substitute_internal::Arg& a3,
344     const substitute_internal::Arg& a4, const substitute_internal::Arg& a5,
345     const substitute_internal::Arg& a6, const substitute_internal::Arg& a7,
346     const substitute_internal::Arg& a8) {
347   const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
348                                     a3.piece(), a4.piece(), a5.piece(),
349                                     a6.piece(), a7.piece(), a8.piece()};
350   substitute_internal::SubstituteAndAppendArray(output, format, args,
351                                                 ABSL_ARRAYSIZE(args));
352 }
353 
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)354 inline void SubstituteAndAppend(
355     std::string* output, absl::string_view format,
356     const substitute_internal::Arg& a0, const substitute_internal::Arg& a1,
357     const substitute_internal::Arg& a2, const substitute_internal::Arg& a3,
358     const substitute_internal::Arg& a4, const substitute_internal::Arg& a5,
359     const substitute_internal::Arg& a6, const substitute_internal::Arg& a7,
360     const substitute_internal::Arg& a8, const substitute_internal::Arg& a9) {
361   const absl::string_view args[] = {
362       a0.piece(), a1.piece(), a2.piece(), a3.piece(), a4.piece(),
363       a5.piece(), a6.piece(), a7.piece(), a8.piece(), a9.piece()};
364   substitute_internal::SubstituteAndAppendArray(output, format, args,
365                                                 ABSL_ARRAYSIZE(args));
366 }
367 
368 #if defined(ABSL_BAD_CALL_IF)
369 // This body of functions catches cases where the number of placeholders
370 // doesn't match the number of data arguments.
371 void SubstituteAndAppend(std::string* output, const char* format)
372     ABSL_BAD_CALL_IF(
373         substitute_internal::PlaceholderBitmask(format) != 0,
374         "There were no substitution arguments "
375         "but this format string either has a $[0-9] in it or contains "
376         "an unescaped $ character (use $$ instead)");
377 
378 void SubstituteAndAppend(std::string* output, const char* format,
379                          const substitute_internal::Arg& a0)
380     ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 1,
381                      "There was 1 substitution argument given, but "
382                      "this format string is missing its $0, contains "
383                      "one of $1-$9, or contains an unescaped $ character (use "
384                      "$$ instead)");
385 
386 void SubstituteAndAppend(std::string* output, const char* format,
387                          const substitute_internal::Arg& a0,
388                          const substitute_internal::Arg& a1)
389     ABSL_BAD_CALL_IF(
390         substitute_internal::PlaceholderBitmask(format) != 3,
391         "There were 2 substitution arguments given, but this format string is "
392         "missing its $0/$1, contains one of $2-$9, or contains an "
393         "unescaped $ character (use $$ instead)");
394 
395 void SubstituteAndAppend(std::string* output, const char* format,
396                          const substitute_internal::Arg& a0,
397                          const substitute_internal::Arg& a1,
398                          const substitute_internal::Arg& a2)
399     ABSL_BAD_CALL_IF(
400         substitute_internal::PlaceholderBitmask(format) != 7,
401         "There were 3 substitution arguments given, but "
402         "this format string is missing its $0/$1/$2, contains one of "
403         "$3-$9, or contains an unescaped $ character (use $$ instead)");
404 
405 void SubstituteAndAppend(std::string* output, const char* format,
406                          const substitute_internal::Arg& a0,
407                          const substitute_internal::Arg& a1,
408                          const substitute_internal::Arg& a2,
409                          const substitute_internal::Arg& a3)
410     ABSL_BAD_CALL_IF(
411         substitute_internal::PlaceholderBitmask(format) != 15,
412         "There were 4 substitution arguments given, but "
413         "this format string is missing its $0-$3, contains one of "
414         "$4-$9, or contains an unescaped $ character (use $$ instead)");
415 
416 void SubstituteAndAppend(std::string* output, const char* format,
417                          const substitute_internal::Arg& a0,
418                          const substitute_internal::Arg& a1,
419                          const substitute_internal::Arg& a2,
420                          const substitute_internal::Arg& a3,
421                          const substitute_internal::Arg& a4)
422     ABSL_BAD_CALL_IF(
423         substitute_internal::PlaceholderBitmask(format) != 31,
424         "There were 5 substitution arguments given, but "
425         "this format string is missing its $0-$4, contains one of "
426         "$5-$9, or contains an unescaped $ character (use $$ instead)");
427 
428 void SubstituteAndAppend(std::string* output, const char* format,
429                          const substitute_internal::Arg& a0,
430                          const substitute_internal::Arg& a1,
431                          const substitute_internal::Arg& a2,
432                          const substitute_internal::Arg& a3,
433                          const substitute_internal::Arg& a4,
434                          const substitute_internal::Arg& a5)
435     ABSL_BAD_CALL_IF(
436         substitute_internal::PlaceholderBitmask(format) != 63,
437         "There were 6 substitution arguments given, but "
438         "this format string is missing its $0-$5, contains one of "
439         "$6-$9, or contains an unescaped $ character (use $$ instead)");
440 
441 void SubstituteAndAppend(
442     std::string* output, const char* format, const substitute_internal::Arg& a0,
443     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
444     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
445     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6)
446     ABSL_BAD_CALL_IF(
447         substitute_internal::PlaceholderBitmask(format) != 127,
448         "There were 7 substitution arguments given, but "
449         "this format string is missing its $0-$6, contains one of "
450         "$7-$9, or contains an unescaped $ character (use $$ instead)");
451 
452 void SubstituteAndAppend(
453     std::string* output, const char* format, const substitute_internal::Arg& a0,
454     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
455     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
456     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
457     const substitute_internal::Arg& a7)
458     ABSL_BAD_CALL_IF(
459         substitute_internal::PlaceholderBitmask(format) != 255,
460         "There were 8 substitution arguments given, but "
461         "this format string is missing its $0-$7, contains one of "
462         "$8-$9, or contains an unescaped $ character (use $$ instead)");
463 
464 void SubstituteAndAppend(
465     std::string* output, const char* format, const substitute_internal::Arg& a0,
466     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
467     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
468     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
469     const substitute_internal::Arg& a7, const substitute_internal::Arg& a8)
470     ABSL_BAD_CALL_IF(
471         substitute_internal::PlaceholderBitmask(format) != 511,
472         "There were 9 substitution arguments given, but "
473         "this format string is missing its $0-$8, contains a $9, or "
474         "contains an unescaped $ character (use $$ instead)");
475 
476 void SubstituteAndAppend(
477     std::string* output, const char* format, const substitute_internal::Arg& a0,
478     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
479     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
480     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
481     const substitute_internal::Arg& a7, const substitute_internal::Arg& a8,
482     const substitute_internal::Arg& a9)
483     ABSL_BAD_CALL_IF(
484         substitute_internal::PlaceholderBitmask(format) != 1023,
485         "There were 10 substitution arguments given, but this "
486         "format string either doesn't contain all of $0 through $9 or "
487         "contains an unescaped $ character (use $$ instead)");
488 #endif  // ABSL_BAD_CALL_IF
489 
490 // Substitute()
491 //
492 // Substitutes variables into a given format string. See file comments above
493 // for usage.
494 //
495 // The declarations of `Substitute()` below consist of overloads for passing 0
496 // to 10 arguments, respectively.
497 //
498 // NOTE: A zero-argument `Substitute()` may be used within variadic templates to
499 // allow a variable number of arguments.
500 //
501 // Example:
502 //  template <typename... Args>
503 //  void VarMsg(absl::string_view format, const Args&... args) {
504 //    std::string s = absl::Substitute(format, args...);
505 
Substitute(absl::string_view format)506 ABSL_MUST_USE_RESULT inline std::string Substitute(absl::string_view format) {
507   std::string result;
508   SubstituteAndAppend(&result, format);
509   return result;
510 }
511 
Substitute(absl::string_view format,const substitute_internal::Arg & a0)512 ABSL_MUST_USE_RESULT inline std::string Substitute(
513     absl::string_view format, const substitute_internal::Arg& a0) {
514   std::string result;
515   SubstituteAndAppend(&result, format, a0);
516   return result;
517 }
518 
Substitute(absl::string_view format,const substitute_internal::Arg & a0,const substitute_internal::Arg & a1)519 ABSL_MUST_USE_RESULT inline std::string Substitute(
520     absl::string_view format, const substitute_internal::Arg& a0,
521     const substitute_internal::Arg& a1) {
522   std::string result;
523   SubstituteAndAppend(&result, format, a0, a1);
524   return result;
525 }
526 
Substitute(absl::string_view format,const substitute_internal::Arg & a0,const substitute_internal::Arg & a1,const substitute_internal::Arg & a2)527 ABSL_MUST_USE_RESULT inline std::string Substitute(
528     absl::string_view format, const substitute_internal::Arg& a0,
529     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2) {
530   std::string result;
531   SubstituteAndAppend(&result, format, a0, a1, a2);
532   return result;
533 }
534 
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)535 ABSL_MUST_USE_RESULT inline std::string Substitute(
536     absl::string_view format, const substitute_internal::Arg& a0,
537     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
538     const substitute_internal::Arg& a3) {
539   std::string result;
540   SubstituteAndAppend(&result, format, a0, a1, a2, a3);
541   return result;
542 }
543 
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)544 ABSL_MUST_USE_RESULT inline std::string Substitute(
545     absl::string_view format, const substitute_internal::Arg& a0,
546     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
547     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4) {
548   std::string result;
549   SubstituteAndAppend(&result, format, a0, a1, a2, a3, a4);
550   return result;
551 }
552 
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)553 ABSL_MUST_USE_RESULT inline std::string Substitute(
554     absl::string_view format, const substitute_internal::Arg& a0,
555     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
556     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
557     const substitute_internal::Arg& a5) {
558   std::string result;
559   SubstituteAndAppend(&result, format, a0, a1, a2, a3, a4, a5);
560   return result;
561 }
562 
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)563 ABSL_MUST_USE_RESULT inline std::string Substitute(
564     absl::string_view format, const substitute_internal::Arg& a0,
565     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
566     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
567     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6) {
568   std::string result;
569   SubstituteAndAppend(&result, format, a0, a1, a2, a3, a4, a5, a6);
570   return result;
571 }
572 
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)573 ABSL_MUST_USE_RESULT inline std::string Substitute(
574     absl::string_view format, const substitute_internal::Arg& a0,
575     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
576     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
577     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
578     const substitute_internal::Arg& a7) {
579   std::string result;
580   SubstituteAndAppend(&result, format, a0, a1, a2, a3, a4, a5, a6, a7);
581   return result;
582 }
583 
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)584 ABSL_MUST_USE_RESULT inline std::string Substitute(
585     absl::string_view format, const substitute_internal::Arg& a0,
586     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
587     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
588     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
589     const substitute_internal::Arg& a7, const substitute_internal::Arg& a8) {
590   std::string result;
591   SubstituteAndAppend(&result, format, a0, a1, a2, a3, a4, a5, a6, a7, a8);
592   return result;
593 }
594 
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)595 ABSL_MUST_USE_RESULT inline std::string Substitute(
596     absl::string_view format, const substitute_internal::Arg& a0,
597     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
598     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
599     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
600     const substitute_internal::Arg& a7, const substitute_internal::Arg& a8,
601     const substitute_internal::Arg& a9) {
602   std::string result;
603   SubstituteAndAppend(&result, format, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
604   return result;
605 }
606 
607 #if defined(ABSL_BAD_CALL_IF)
608 // This body of functions catches cases where the number of placeholders
609 // doesn't match the number of data arguments.
610 std::string Substitute(const char* format)
611     ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 0,
612                      "There were no substitution arguments "
613                      "but this format string either has a $[0-9] in it or "
614                      "contains an unescaped $ character (use $$ instead)");
615 
616 std::string Substitute(const char* format, const substitute_internal::Arg& a0)
617     ABSL_BAD_CALL_IF(
618         substitute_internal::PlaceholderBitmask(format) != 1,
619         "There was 1 substitution argument given, but "
620         "this format string is missing its $0, contains one of $1-$9, "
621         "or contains an unescaped $ character (use $$ instead)");
622 
623 std::string Substitute(const char* format, const substitute_internal::Arg& a0,
624                        const substitute_internal::Arg& a1)
625     ABSL_BAD_CALL_IF(
626         substitute_internal::PlaceholderBitmask(format) != 3,
627         "There were 2 substitution arguments given, but "
628         "this format string is missing its $0/$1, contains one of "
629         "$2-$9, or contains an unescaped $ character (use $$ instead)");
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     ABSL_BAD_CALL_IF(
635         substitute_internal::PlaceholderBitmask(format) != 7,
636         "There were 3 substitution arguments given, but "
637         "this format string is missing its $0/$1/$2, contains one of "
638         "$3-$9, or contains an unescaped $ character (use $$ instead)");
639 
640 std::string Substitute(const char* format, const substitute_internal::Arg& a0,
641                        const substitute_internal::Arg& a1,
642                        const substitute_internal::Arg& a2,
643                        const substitute_internal::Arg& a3)
644     ABSL_BAD_CALL_IF(
645         substitute_internal::PlaceholderBitmask(format) != 15,
646         "There were 4 substitution arguments given, but "
647         "this format string is missing its $0-$3, contains one of "
648         "$4-$9, or contains an unescaped $ character (use $$ instead)");
649 
650 std::string Substitute(const char* format, const substitute_internal::Arg& a0,
651                        const substitute_internal::Arg& a1,
652                        const substitute_internal::Arg& a2,
653                        const substitute_internal::Arg& a3,
654                        const substitute_internal::Arg& a4)
655     ABSL_BAD_CALL_IF(
656         substitute_internal::PlaceholderBitmask(format) != 31,
657         "There were 5 substitution arguments given, but "
658         "this format string is missing its $0-$4, contains one of "
659         "$5-$9, or contains an unescaped $ character (use $$ instead)");
660 
661 std::string Substitute(const char* format, const substitute_internal::Arg& a0,
662                        const substitute_internal::Arg& a1,
663                        const substitute_internal::Arg& a2,
664                        const substitute_internal::Arg& a3,
665                        const substitute_internal::Arg& a4,
666                        const substitute_internal::Arg& a5)
667     ABSL_BAD_CALL_IF(
668         substitute_internal::PlaceholderBitmask(format) != 63,
669         "There were 6 substitution arguments given, but "
670         "this format string is missing its $0-$5, contains one of "
671         "$6-$9, or contains an unescaped $ character (use $$ instead)");
672 
673 std::string Substitute(const char* format, const substitute_internal::Arg& a0,
674                        const substitute_internal::Arg& a1,
675                        const substitute_internal::Arg& a2,
676                        const substitute_internal::Arg& a3,
677                        const substitute_internal::Arg& a4,
678                        const substitute_internal::Arg& a5,
679                        const substitute_internal::Arg& a6)
680     ABSL_BAD_CALL_IF(
681         substitute_internal::PlaceholderBitmask(format) != 127,
682         "There were 7 substitution arguments given, but "
683         "this format string is missing its $0-$6, contains one of "
684         "$7-$9, or contains an unescaped $ character (use $$ instead)");
685 
686 std::string Substitute(const char* format, const substitute_internal::Arg& a0,
687                        const substitute_internal::Arg& a1,
688                        const substitute_internal::Arg& a2,
689                        const substitute_internal::Arg& a3,
690                        const substitute_internal::Arg& a4,
691                        const substitute_internal::Arg& a5,
692                        const substitute_internal::Arg& a6,
693                        const substitute_internal::Arg& a7)
694     ABSL_BAD_CALL_IF(
695         substitute_internal::PlaceholderBitmask(format) != 255,
696         "There were 8 substitution arguments given, but "
697         "this format string is missing its $0-$7, contains one of "
698         "$8-$9, or contains an unescaped $ character (use $$ instead)");
699 
700 std::string Substitute(
701     const char* format, const substitute_internal::Arg& a0,
702     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
703     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
704     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
705     const substitute_internal::Arg& a7, const substitute_internal::Arg& a8)
706     ABSL_BAD_CALL_IF(
707         substitute_internal::PlaceholderBitmask(format) != 511,
708         "There were 9 substitution arguments given, but "
709         "this format string is missing its $0-$8, contains a $9, or "
710         "contains an unescaped $ character (use $$ instead)");
711 
712 std::string Substitute(
713     const char* format, const substitute_internal::Arg& a0,
714     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
715     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
716     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
717     const substitute_internal::Arg& a7, const substitute_internal::Arg& a8,
718     const substitute_internal::Arg& a9)
719     ABSL_BAD_CALL_IF(
720         substitute_internal::PlaceholderBitmask(format) != 1023,
721         "There were 10 substitution arguments given, but this "
722         "format string either doesn't contain all of $0 through $9 or "
723         "contains an unescaped $ character (use $$ instead)");
724 #endif  // ABSL_BAD_CALL_IF
725 
726 ABSL_NAMESPACE_END
727 }  // namespace absl
728 
729 #endif  // ABSL_STRINGS_SUBSTITUTE_H_
730