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
163   // convert to `AlphaNum` 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   Arg(const Arg&) = delete;
178   Arg& operator=(const Arg&) = delete;
179 
piece()180   absl::string_view piece() const { return piece_; }
181 
182  private:
183   absl::string_view piece_;
184   char scratch_[numbers_internal::kFastToBufferSize];
185 };
186 
187 // Internal helper function. Don't call this from outside this implementation.
188 // This interface may change without notice.
189 void SubstituteAndAppendArray(std::string* output, absl::string_view format,
190                               const absl::string_view* args_array,
191                               size_t num_args);
192 
193 #if defined(ABSL_BAD_CALL_IF)
CalculateOneBit(const char * format)194 constexpr int CalculateOneBit(const char* format) {
195   // Returns:
196   // * 2^N for '$N' when N is in [0-9]
197   // * 0 for correct '$' escaping: '$$'.
198   // * -1 otherwise.
199   return (*format < '0' || *format > '9') ? (*format == '$' ? 0 : -1)
200                                           : (1 << (*format - '0'));
201 }
202 
SkipNumber(const char * format)203 constexpr const char* SkipNumber(const char* format) {
204   return !*format ? format : (format + 1);
205 }
206 
PlaceholderBitmask(const char * format)207 constexpr int PlaceholderBitmask(const char* format) {
208   return !*format
209              ? 0
210              : *format != '$' ? PlaceholderBitmask(format + 1)
211                               : (CalculateOneBit(format + 1) |
212                                  PlaceholderBitmask(SkipNumber(format + 1)));
213 }
214 #endif  // ABSL_BAD_CALL_IF
215 
216 }  // namespace substitute_internal
217 
218 //
219 // PUBLIC API
220 //
221 
222 // SubstituteAndAppend()
223 //
224 // Substitutes variables into a given format string and appends to a given
225 // output string. See file comments above for usage.
226 //
227 // The declarations of `SubstituteAndAppend()` below consist of overloads
228 // for passing 0 to 10 arguments, respectively.
229 //
230 // NOTE: A zero-argument `SubstituteAndAppend()` may be used within variadic
231 // templates to allow a variable number of arguments.
232 //
233 // Example:
234 //  template <typename... Args>
235 //  void VarMsg(std::string* boilerplate, absl::string_view format,
236 //      const Args&... args) {
237 //    absl::SubstituteAndAppend(boilerplate, format, args...);
238 //  }
239 //
SubstituteAndAppend(std::string * output,absl::string_view format)240 inline void SubstituteAndAppend(std::string* output, absl::string_view format) {
241   substitute_internal::SubstituteAndAppendArray(output, format, nullptr, 0);
242 }
243 
SubstituteAndAppend(std::string * output,absl::string_view format,const substitute_internal::Arg & a0)244 inline void SubstituteAndAppend(std::string* output, absl::string_view format,
245                                 const substitute_internal::Arg& a0) {
246   const absl::string_view args[] = {a0.piece()};
247   substitute_internal::SubstituteAndAppendArray(output, format, args,
248                                                 ABSL_ARRAYSIZE(args));
249 }
250 
SubstituteAndAppend(std::string * output,absl::string_view format,const substitute_internal::Arg & a0,const substitute_internal::Arg & a1)251 inline void SubstituteAndAppend(std::string* output, absl::string_view format,
252                                 const substitute_internal::Arg& a0,
253                                 const substitute_internal::Arg& a1) {
254   const absl::string_view args[] = {a0.piece(), a1.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,const substitute_internal::Arg & a2)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 substitute_internal::Arg& a2) {
263   const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece()};
264   substitute_internal::SubstituteAndAppendArray(output, format, args,
265                                                 ABSL_ARRAYSIZE(args));
266 }
267 
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)268 inline void SubstituteAndAppend(std::string* output, absl::string_view format,
269                                 const substitute_internal::Arg& a0,
270                                 const substitute_internal::Arg& a1,
271                                 const substitute_internal::Arg& a2,
272                                 const substitute_internal::Arg& a3) {
273   const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
274                                     a3.piece()};
275   substitute_internal::SubstituteAndAppendArray(output, format, args,
276                                                 ABSL_ARRAYSIZE(args));
277 }
278 
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)279 inline void SubstituteAndAppend(std::string* output, absl::string_view format,
280                                 const substitute_internal::Arg& a0,
281                                 const substitute_internal::Arg& a1,
282                                 const substitute_internal::Arg& a2,
283                                 const substitute_internal::Arg& a3,
284                                 const substitute_internal::Arg& a4) {
285   const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
286                                     a3.piece(), a4.piece()};
287   substitute_internal::SubstituteAndAppendArray(output, format, args,
288                                                 ABSL_ARRAYSIZE(args));
289 }
290 
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)291 inline void SubstituteAndAppend(std::string* output, absl::string_view format,
292                                 const substitute_internal::Arg& a0,
293                                 const substitute_internal::Arg& a1,
294                                 const substitute_internal::Arg& a2,
295                                 const substitute_internal::Arg& a3,
296                                 const substitute_internal::Arg& a4,
297                                 const substitute_internal::Arg& a5) {
298   const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
299                                     a3.piece(), a4.piece(), a5.piece()};
300   substitute_internal::SubstituteAndAppendArray(output, format, args,
301                                                 ABSL_ARRAYSIZE(args));
302 }
303 
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)304 inline void SubstituteAndAppend(std::string* output, absl::string_view format,
305                                 const substitute_internal::Arg& a0,
306                                 const substitute_internal::Arg& a1,
307                                 const substitute_internal::Arg& a2,
308                                 const substitute_internal::Arg& a3,
309                                 const substitute_internal::Arg& a4,
310                                 const substitute_internal::Arg& a5,
311                                 const substitute_internal::Arg& a6) {
312   const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
313                                     a3.piece(), a4.piece(), a5.piece(),
314                                     a6.piece()};
315   substitute_internal::SubstituteAndAppendArray(output, format, args,
316                                                 ABSL_ARRAYSIZE(args));
317 }
318 
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)319 inline void SubstituteAndAppend(
320     std::string* output, absl::string_view format,
321     const substitute_internal::Arg& a0, const substitute_internal::Arg& a1,
322     const substitute_internal::Arg& a2, const substitute_internal::Arg& a3,
323     const substitute_internal::Arg& a4, const substitute_internal::Arg& a5,
324     const substitute_internal::Arg& a6, const substitute_internal::Arg& a7) {
325   const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
326                                     a3.piece(), a4.piece(), a5.piece(),
327                                     a6.piece(), a7.piece()};
328   substitute_internal::SubstituteAndAppendArray(output, format, args,
329                                                 ABSL_ARRAYSIZE(args));
330 }
331 
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)332 inline void SubstituteAndAppend(
333     std::string* output, absl::string_view format,
334     const substitute_internal::Arg& a0, const substitute_internal::Arg& a1,
335     const substitute_internal::Arg& a2, const substitute_internal::Arg& a3,
336     const substitute_internal::Arg& a4, const substitute_internal::Arg& a5,
337     const substitute_internal::Arg& a6, const substitute_internal::Arg& a7,
338     const substitute_internal::Arg& a8) {
339   const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
340                                     a3.piece(), a4.piece(), a5.piece(),
341                                     a6.piece(), a7.piece(), a8.piece()};
342   substitute_internal::SubstituteAndAppendArray(output, format, args,
343                                                 ABSL_ARRAYSIZE(args));
344 }
345 
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)346 inline void SubstituteAndAppend(
347     std::string* output, absl::string_view format,
348     const substitute_internal::Arg& a0, const substitute_internal::Arg& a1,
349     const substitute_internal::Arg& a2, const substitute_internal::Arg& a3,
350     const substitute_internal::Arg& a4, const substitute_internal::Arg& a5,
351     const substitute_internal::Arg& a6, const substitute_internal::Arg& a7,
352     const substitute_internal::Arg& a8, const substitute_internal::Arg& a9) {
353   const absl::string_view args[] = {
354       a0.piece(), a1.piece(), a2.piece(), a3.piece(), a4.piece(),
355       a5.piece(), a6.piece(), a7.piece(), a8.piece(), a9.piece()};
356   substitute_internal::SubstituteAndAppendArray(output, format, args,
357                                                 ABSL_ARRAYSIZE(args));
358 }
359 
360 #if defined(ABSL_BAD_CALL_IF)
361 // This body of functions catches cases where the number of placeholders
362 // doesn't match the number of data arguments.
363 void SubstituteAndAppend(std::string* output, const char* format)
364     ABSL_BAD_CALL_IF(
365         substitute_internal::PlaceholderBitmask(format) != 0,
366         "There were no substitution arguments "
367         "but this format string either has a $[0-9] in it or contains "
368         "an unescaped $ character (use $$ instead)");
369 
370 void SubstituteAndAppend(std::string* output, const char* format,
371                          const substitute_internal::Arg& a0)
372     ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 1,
373                      "There was 1 substitution argument given, but "
374                      "this format string is missing its $0, contains "
375                      "one of $1-$9, or contains an unescaped $ character (use "
376                      "$$ instead)");
377 
378 void SubstituteAndAppend(std::string* output, const char* format,
379                          const substitute_internal::Arg& a0,
380                          const substitute_internal::Arg& a1)
381     ABSL_BAD_CALL_IF(
382         substitute_internal::PlaceholderBitmask(format) != 3,
383         "There were 2 substitution arguments given, but this format string is "
384         "missing its $0/$1, contains one of $2-$9, or contains an "
385         "unescaped $ character (use $$ instead)");
386 
387 void SubstituteAndAppend(std::string* output, const char* format,
388                          const substitute_internal::Arg& a0,
389                          const substitute_internal::Arg& a1,
390                          const substitute_internal::Arg& a2)
391     ABSL_BAD_CALL_IF(
392         substitute_internal::PlaceholderBitmask(format) != 7,
393         "There were 3 substitution arguments given, but "
394         "this format string is missing its $0/$1/$2, contains one of "
395         "$3-$9, or contains an unescaped $ character (use $$ instead)");
396 
397 void SubstituteAndAppend(std::string* output, const char* format,
398                          const substitute_internal::Arg& a0,
399                          const substitute_internal::Arg& a1,
400                          const substitute_internal::Arg& a2,
401                          const substitute_internal::Arg& a3)
402     ABSL_BAD_CALL_IF(
403         substitute_internal::PlaceholderBitmask(format) != 15,
404         "There were 4 substitution arguments given, but "
405         "this format string is missing its $0-$3, contains one of "
406         "$4-$9, or contains an unescaped $ character (use $$ instead)");
407 
408 void SubstituteAndAppend(std::string* output, const char* format,
409                          const substitute_internal::Arg& a0,
410                          const substitute_internal::Arg& a1,
411                          const substitute_internal::Arg& a2,
412                          const substitute_internal::Arg& a3,
413                          const substitute_internal::Arg& a4)
414     ABSL_BAD_CALL_IF(
415         substitute_internal::PlaceholderBitmask(format) != 31,
416         "There were 5 substitution arguments given, but "
417         "this format string is missing its $0-$4, contains one of "
418         "$5-$9, or contains an unescaped $ character (use $$ instead)");
419 
420 void SubstituteAndAppend(std::string* output, const char* format,
421                          const substitute_internal::Arg& a0,
422                          const substitute_internal::Arg& a1,
423                          const substitute_internal::Arg& a2,
424                          const substitute_internal::Arg& a3,
425                          const substitute_internal::Arg& a4,
426                          const substitute_internal::Arg& a5)
427     ABSL_BAD_CALL_IF(
428         substitute_internal::PlaceholderBitmask(format) != 63,
429         "There were 6 substitution arguments given, but "
430         "this format string is missing its $0-$5, contains one of "
431         "$6-$9, or contains an unescaped $ character (use $$ instead)");
432 
433 void SubstituteAndAppend(
434     std::string* output, const char* format, const substitute_internal::Arg& a0,
435     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
436     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
437     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6)
438     ABSL_BAD_CALL_IF(
439         substitute_internal::PlaceholderBitmask(format) != 127,
440         "There were 7 substitution arguments given, but "
441         "this format string is missing its $0-$6, contains one of "
442         "$7-$9, or contains an unescaped $ character (use $$ instead)");
443 
444 void SubstituteAndAppend(
445     std::string* output, const char* format, const substitute_internal::Arg& a0,
446     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
447     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
448     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
449     const substitute_internal::Arg& a7)
450     ABSL_BAD_CALL_IF(
451         substitute_internal::PlaceholderBitmask(format) != 255,
452         "There were 8 substitution arguments given, but "
453         "this format string is missing its $0-$7, contains one of "
454         "$8-$9, or contains an unescaped $ character (use $$ instead)");
455 
456 void SubstituteAndAppend(
457     std::string* output, const char* format, const substitute_internal::Arg& a0,
458     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
459     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
460     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
461     const substitute_internal::Arg& a7, const substitute_internal::Arg& a8)
462     ABSL_BAD_CALL_IF(
463         substitute_internal::PlaceholderBitmask(format) != 511,
464         "There were 9 substitution arguments given, but "
465         "this format string is missing its $0-$8, contains a $9, or "
466         "contains an unescaped $ character (use $$ instead)");
467 
468 void SubstituteAndAppend(
469     std::string* output, const char* format, const substitute_internal::Arg& a0,
470     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
471     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
472     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
473     const substitute_internal::Arg& a7, const substitute_internal::Arg& a8,
474     const substitute_internal::Arg& a9)
475     ABSL_BAD_CALL_IF(
476         substitute_internal::PlaceholderBitmask(format) != 1023,
477         "There were 10 substitution arguments given, but this "
478         "format string either doesn't contain all of $0 through $9 or "
479         "contains an unescaped $ character (use $$ instead)");
480 #endif  // ABSL_BAD_CALL_IF
481 
482 // Substitute()
483 //
484 // Substitutes variables into a given format string. See file comments above
485 // for usage.
486 //
487 // The declarations of `Substitute()` below consist of overloads for passing 0
488 // to 10 arguments, respectively.
489 //
490 // NOTE: A zero-argument `Substitute()` may be used within variadic templates to
491 // allow a variable number of arguments.
492 //
493 // Example:
494 //  template <typename... Args>
495 //  void VarMsg(absl::string_view format, const Args&... args) {
496 //    std::string s = absl::Substitute(format, args...);
497 
Substitute(absl::string_view format)498 ABSL_MUST_USE_RESULT inline std::string Substitute(absl::string_view format) {
499   std::string result;
500   SubstituteAndAppend(&result, format);
501   return result;
502 }
503 
Substitute(absl::string_view format,const substitute_internal::Arg & a0)504 ABSL_MUST_USE_RESULT inline std::string Substitute(
505     absl::string_view format, const substitute_internal::Arg& a0) {
506   std::string result;
507   SubstituteAndAppend(&result, format, a0);
508   return result;
509 }
510 
Substitute(absl::string_view format,const substitute_internal::Arg & a0,const substitute_internal::Arg & a1)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) {
514   std::string result;
515   SubstituteAndAppend(&result, format, a0, a1);
516   return result;
517 }
518 
Substitute(absl::string_view format,const substitute_internal::Arg & a0,const substitute_internal::Arg & a1,const substitute_internal::Arg & a2)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, const substitute_internal::Arg& a2) {
522   std::string result;
523   SubstituteAndAppend(&result, format, a0, a1, a2);
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,const substitute_internal::Arg & a3)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     const substitute_internal::Arg& a3) {
531   std::string result;
532   SubstituteAndAppend(&result, format, a0, a1, a2, a3);
533   return result;
534 }
535 
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)536 ABSL_MUST_USE_RESULT inline std::string Substitute(
537     absl::string_view format, const substitute_internal::Arg& a0,
538     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
539     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4) {
540   std::string result;
541   SubstituteAndAppend(&result, format, a0, a1, a2, a3, a4);
542   return result;
543 }
544 
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)545 ABSL_MUST_USE_RESULT inline std::string Substitute(
546     absl::string_view format, const substitute_internal::Arg& a0,
547     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
548     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
549     const substitute_internal::Arg& a5) {
550   std::string result;
551   SubstituteAndAppend(&result, format, a0, a1, a2, a3, a4, a5);
552   return result;
553 }
554 
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)555 ABSL_MUST_USE_RESULT inline std::string Substitute(
556     absl::string_view format, const substitute_internal::Arg& a0,
557     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
558     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
559     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6) {
560   std::string result;
561   SubstituteAndAppend(&result, format, a0, a1, a2, a3, a4, a5, a6);
562   return result;
563 }
564 
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)565 ABSL_MUST_USE_RESULT inline std::string Substitute(
566     absl::string_view format, const substitute_internal::Arg& a0,
567     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
568     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
569     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
570     const substitute_internal::Arg& a7) {
571   std::string result;
572   SubstituteAndAppend(&result, format, a0, a1, a2, a3, a4, a5, a6, a7);
573   return result;
574 }
575 
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)576 ABSL_MUST_USE_RESULT inline std::string Substitute(
577     absl::string_view format, const substitute_internal::Arg& a0,
578     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
579     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
580     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
581     const substitute_internal::Arg& a7, const substitute_internal::Arg& a8) {
582   std::string result;
583   SubstituteAndAppend(&result, format, a0, a1, a2, a3, a4, a5, a6, a7, a8);
584   return result;
585 }
586 
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)587 ABSL_MUST_USE_RESULT inline std::string Substitute(
588     absl::string_view format, const substitute_internal::Arg& a0,
589     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
590     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
591     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
592     const substitute_internal::Arg& a7, const substitute_internal::Arg& a8,
593     const substitute_internal::Arg& a9) {
594   std::string result;
595   SubstituteAndAppend(&result, format, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
596   return result;
597 }
598 
599 #if defined(ABSL_BAD_CALL_IF)
600 // This body of functions catches cases where the number of placeholders
601 // doesn't match the number of data arguments.
602 std::string Substitute(const char* format)
603     ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 0,
604                      "There were no substitution arguments "
605                      "but this format string either has a $[0-9] in it or "
606                      "contains an unescaped $ character (use $$ instead)");
607 
608 std::string Substitute(const char* format, const substitute_internal::Arg& a0)
609     ABSL_BAD_CALL_IF(
610         substitute_internal::PlaceholderBitmask(format) != 1,
611         "There was 1 substitution argument given, but "
612         "this format string is missing its $0, contains one of $1-$9, "
613         "or contains an unescaped $ character (use $$ instead)");
614 
615 std::string Substitute(const char* format, const substitute_internal::Arg& a0,
616                        const substitute_internal::Arg& a1)
617     ABSL_BAD_CALL_IF(
618         substitute_internal::PlaceholderBitmask(format) != 3,
619         "There were 2 substitution arguments given, but "
620         "this format string is missing its $0/$1, contains one of "
621         "$2-$9, 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                        const substitute_internal::Arg& a2)
626     ABSL_BAD_CALL_IF(
627         substitute_internal::PlaceholderBitmask(format) != 7,
628         "There were 3 substitution arguments given, but "
629         "this format string is missing its $0/$1/$2, contains one of "
630         "$3-$9, or contains an unescaped $ character (use $$ instead)");
631 
632 std::string Substitute(const char* format, const substitute_internal::Arg& a0,
633                        const substitute_internal::Arg& a1,
634                        const substitute_internal::Arg& a2,
635                        const substitute_internal::Arg& a3)
636     ABSL_BAD_CALL_IF(
637         substitute_internal::PlaceholderBitmask(format) != 15,
638         "There were 4 substitution arguments given, but "
639         "this format string is missing its $0-$3, contains one of "
640         "$4-$9, or contains an unescaped $ character (use $$ instead)");
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     ABSL_BAD_CALL_IF(
648         substitute_internal::PlaceholderBitmask(format) != 31,
649         "There were 5 substitution arguments given, but "
650         "this format string is missing its $0-$4, contains one of "
651         "$5-$9, or contains an unescaped $ character (use $$ instead)");
652 
653 std::string Substitute(const char* format, const substitute_internal::Arg& a0,
654                        const substitute_internal::Arg& a1,
655                        const substitute_internal::Arg& a2,
656                        const substitute_internal::Arg& a3,
657                        const substitute_internal::Arg& a4,
658                        const substitute_internal::Arg& a5)
659     ABSL_BAD_CALL_IF(
660         substitute_internal::PlaceholderBitmask(format) != 63,
661         "There were 6 substitution arguments given, but "
662         "this format string is missing its $0-$5, contains one of "
663         "$6-$9, or contains an unescaped $ character (use $$ instead)");
664 
665 std::string Substitute(const char* format, const substitute_internal::Arg& a0,
666                        const substitute_internal::Arg& a1,
667                        const substitute_internal::Arg& a2,
668                        const substitute_internal::Arg& a3,
669                        const substitute_internal::Arg& a4,
670                        const substitute_internal::Arg& a5,
671                        const substitute_internal::Arg& a6)
672     ABSL_BAD_CALL_IF(
673         substitute_internal::PlaceholderBitmask(format) != 127,
674         "There were 7 substitution arguments given, but "
675         "this format string is missing its $0-$6, contains one of "
676         "$7-$9, or contains an unescaped $ character (use $$ instead)");
677 
678 std::string Substitute(const char* format, const substitute_internal::Arg& a0,
679                        const substitute_internal::Arg& a1,
680                        const substitute_internal::Arg& a2,
681                        const substitute_internal::Arg& a3,
682                        const substitute_internal::Arg& a4,
683                        const substitute_internal::Arg& a5,
684                        const substitute_internal::Arg& a6,
685                        const substitute_internal::Arg& a7)
686     ABSL_BAD_CALL_IF(
687         substitute_internal::PlaceholderBitmask(format) != 255,
688         "There were 8 substitution arguments given, but "
689         "this format string is missing its $0-$7, contains one of "
690         "$8-$9, or contains an unescaped $ character (use $$ instead)");
691 
692 std::string Substitute(
693     const char* format, const substitute_internal::Arg& a0,
694     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
695     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
696     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
697     const substitute_internal::Arg& a7, const substitute_internal::Arg& a8)
698     ABSL_BAD_CALL_IF(
699         substitute_internal::PlaceholderBitmask(format) != 511,
700         "There were 9 substitution arguments given, but "
701         "this format string is missing its $0-$8, contains a $9, or "
702         "contains an unescaped $ character (use $$ instead)");
703 
704 std::string Substitute(
705     const char* format, const substitute_internal::Arg& a0,
706     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
707     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
708     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
709     const substitute_internal::Arg& a7, const substitute_internal::Arg& a8,
710     const substitute_internal::Arg& a9)
711     ABSL_BAD_CALL_IF(
712         substitute_internal::PlaceholderBitmask(format) != 1023,
713         "There were 10 substitution arguments given, but this "
714         "format string either doesn't contain all of $0 through $9 or "
715         "contains an unescaped $ character (use $$ instead)");
716 #endif  // ABSL_BAD_CALL_IF
717 
718 ABSL_NAMESPACE_END
719 }  // namespace absl
720 
721 #endif  // ABSL_STRINGS_SUBSTITUTE_H_
722