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 //   * user-defined types via the `AbslStringify()` customization point. See the
59 //     documentation for `absl::StrCat` for an explanation on how to use this.
60 //
61 // If an invalid format string is provided, Substitute returns an empty string
62 // and SubstituteAndAppend does not change the provided output string.
63 // A format string is invalid if it:
64 //   * ends in an unescaped $ character,
65 //     e.g. "Hello $", or
66 //   * calls for a position argument which is not provided,
67 //     e.g. Substitute("Hello $2", "world"), or
68 //   * specifies a non-digit, non-$ character after an unescaped $ character,
69 //     e.g. "Hello $f".
70 // In debug mode, i.e. #ifndef NDEBUG, such errors terminate the program.
71 
72 #ifndef ABSL_STRINGS_SUBSTITUTE_H_
73 #define ABSL_STRINGS_SUBSTITUTE_H_
74 
75 #include <cstring>
76 #include <string>
77 #include <type_traits>
78 #include <vector>
79 
80 #include "absl/base/macros.h"
81 #include "absl/base/port.h"
82 #include "absl/strings/ascii.h"
83 #include "absl/strings/escaping.h"
84 #include "absl/strings/internal/stringify_sink.h"
85 #include "absl/strings/numbers.h"
86 #include "absl/strings/str_cat.h"
87 #include "absl/strings/str_split.h"
88 #include "absl/strings/string_view.h"
89 #include "absl/strings/strip.h"
90 
91 namespace absl {
92 ABSL_NAMESPACE_BEGIN
93 namespace substitute_internal {
94 
95 // Arg
96 //
97 // This class provides an argument type for `absl::Substitute()` and
98 // `absl::SubstituteAndAppend()`. `Arg` handles implicit conversion of various
99 // types to a string. (`Arg` is very similar to the `AlphaNum` class in
100 // `StrCat()`.)
101 //
102 // This class has implicit constructors.
103 class Arg {
104  public:
105   // Overloads for string-y things
106   //
107   // Explicitly overload `const char*` so the compiler doesn't cast to `bool`.
Arg(const char * value)108   Arg(const char* value)  // NOLINT(google-explicit-constructor)
109       : piece_(absl::NullSafeStringView(value)) {}
110   template <typename Allocator>
Arg(const std::basic_string<char,std::char_traits<char>,Allocator> & value)111   Arg(  // NOLINT
112       const std::basic_string<char, std::char_traits<char>, Allocator>&
113           value) noexcept
114       : piece_(value) {}
Arg(absl::string_view value)115   Arg(absl::string_view value)  // NOLINT(google-explicit-constructor)
116       : piece_(value) {}
117 
118   // Overloads for primitives
119   //
120   // No overloads are available for signed and unsigned char because if people
121   // are explicitly declaring their chars as signed or unsigned then they are
122   // probably using them as 8-bit integers and would probably prefer an integer
123   // representation. However, we can't really know, so we make the caller decide
124   // what to do.
Arg(char value)125   Arg(char value)  // NOLINT(google-explicit-constructor)
126       : piece_(scratch_, 1) {
127     scratch_[0] = value;
128   }
Arg(short value)129   Arg(short value)  // NOLINT(*)
130       : piece_(scratch_,
131                static_cast<size_t>(
132                    numbers_internal::FastIntToBuffer(value, scratch_) -
133                    scratch_)) {}
Arg(unsigned short value)134   Arg(unsigned short value)  // NOLINT(*)
135       : piece_(scratch_,
136                static_cast<size_t>(
137                    numbers_internal::FastIntToBuffer(value, scratch_) -
138                    scratch_)) {}
Arg(int value)139   Arg(int value)  // NOLINT(google-explicit-constructor)
140       : piece_(scratch_,
141                static_cast<size_t>(
142                    numbers_internal::FastIntToBuffer(value, scratch_) -
143                    scratch_)) {}
Arg(unsigned int value)144   Arg(unsigned int value)  // NOLINT(google-explicit-constructor)
145       : piece_(scratch_,
146                static_cast<size_t>(
147                    numbers_internal::FastIntToBuffer(value, scratch_) -
148                    scratch_)) {}
Arg(long value)149   Arg(long value)  // NOLINT(*)
150       : piece_(scratch_,
151                static_cast<size_t>(
152                    numbers_internal::FastIntToBuffer(value, scratch_) -
153                    scratch_)) {}
Arg(unsigned long value)154   Arg(unsigned long value)  // NOLINT(*)
155       : piece_(scratch_,
156                static_cast<size_t>(
157                    numbers_internal::FastIntToBuffer(value, scratch_) -
158                    scratch_)) {}
Arg(long long value)159   Arg(long long value)  // NOLINT(*)
160       : piece_(scratch_,
161                static_cast<size_t>(
162                    numbers_internal::FastIntToBuffer(value, scratch_) -
163                    scratch_)) {}
Arg(unsigned long long value)164   Arg(unsigned long long value)  // NOLINT(*)
165       : piece_(scratch_,
166                static_cast<size_t>(
167                    numbers_internal::FastIntToBuffer(value, scratch_) -
168                    scratch_)) {}
Arg(float value)169   Arg(float value)  // NOLINT(google-explicit-constructor)
170       : piece_(scratch_, numbers_internal::SixDigitsToBuffer(value, scratch_)) {
171   }
Arg(double value)172   Arg(double value)  // NOLINT(google-explicit-constructor)
173       : piece_(scratch_, numbers_internal::SixDigitsToBuffer(value, scratch_)) {
174   }
Arg(bool value)175   Arg(bool value)  // NOLINT(google-explicit-constructor)
176       : piece_(value ? "true" : "false") {}
177 
178   template <typename T, typename = typename std::enable_if<
179                             strings_internal::HasAbslStringify<T>::value>::type>
180   Arg(  // NOLINT(google-explicit-constructor)
181       const T& v, strings_internal::StringifySink&& sink = {})
piece_(strings_internal::ExtractStringification (sink,v))182       : piece_(strings_internal::ExtractStringification(sink, v)) {}
183 
184   Arg(Hex hex);  // NOLINT(google-explicit-constructor)
185   Arg(Dec dec);  // NOLINT(google-explicit-constructor)
186 
187   // vector<bool>::reference and const_reference require special help to convert
188   // to `Arg` because it requires two user defined conversions.
189   template <typename T,
190             absl::enable_if_t<
191                 std::is_class<T>::value &&
192                 (std::is_same<T, std::vector<bool>::reference>::value ||
193                  std::is_same<T, std::vector<bool>::const_reference>::value)>* =
194                 nullptr>
Arg(T value)195   Arg(T value)  // NOLINT(google-explicit-constructor)
196       : Arg(static_cast<bool>(value)) {}
197 
198   // `void*` values, with the exception of `char*`, are printed as
199   // "0x<hex value>". However, in the case of `nullptr`, "NULL" is printed.
200   Arg(const void* value);  // NOLINT(google-explicit-constructor)
201 
202   // Normal enums are already handled by the integer formatters.
203   // This overload matches only scoped enums.
204   template <typename T,
205             typename = typename std::enable_if<
206                 std::is_enum<T>{} && !std::is_convertible<T, int>{} &&
207                 !strings_internal::HasAbslStringify<T>::value>::type>
208   Arg(T value)  // NOLINT(google-explicit-constructor)
209       : Arg(static_cast<typename std::underlying_type<T>::type>(value)) {}
210 
211   Arg(const Arg&) = delete;
212   Arg& operator=(const Arg&) = delete;
213 
piece()214   absl::string_view piece() const { return piece_; }
215 
216  private:
217   absl::string_view piece_;
218   char scratch_[numbers_internal::kFastToBufferSize];
219 };
220 
221 // Internal helper function. Don't call this from outside this implementation.
222 // This interface may change without notice.
223 void SubstituteAndAppendArray(std::string* output, absl::string_view format,
224                               const absl::string_view* args_array,
225                               size_t num_args);
226 
227 #if defined(ABSL_BAD_CALL_IF)
CalculateOneBit(const char * format)228 constexpr int CalculateOneBit(const char* format) {
229   // Returns:
230   // * 2^N for '$N' when N is in [0-9]
231   // * 0 for correct '$' escaping: '$$'.
232   // * -1 otherwise.
233   return (*format < '0' || *format > '9') ? (*format == '$' ? 0 : -1)
234                                           : (1 << (*format - '0'));
235 }
236 
SkipNumber(const char * format)237 constexpr const char* SkipNumber(const char* format) {
238   return !*format ? format : (format + 1);
239 }
240 
PlaceholderBitmask(const char * format)241 constexpr int PlaceholderBitmask(const char* format) {
242   return !*format
243              ? 0
244              : *format != '$' ? PlaceholderBitmask(format + 1)
245                               : (CalculateOneBit(format + 1) |
246                                  PlaceholderBitmask(SkipNumber(format + 1)));
247 }
248 #endif  // ABSL_BAD_CALL_IF
249 
250 }  // namespace substitute_internal
251 
252 //
253 // PUBLIC API
254 //
255 
256 // SubstituteAndAppend()
257 //
258 // Substitutes variables into a given format string and appends to a given
259 // output string. See file comments above for usage.
260 //
261 // The declarations of `SubstituteAndAppend()` below consist of overloads
262 // for passing 0 to 10 arguments, respectively.
263 //
264 // NOTE: A zero-argument `SubstituteAndAppend()` may be used within variadic
265 // templates to allow a variable number of arguments.
266 //
267 // Example:
268 //  template <typename... Args>
269 //  void VarMsg(std::string* boilerplate, absl::string_view format,
270 //      const Args&... args) {
271 //    absl::SubstituteAndAppend(boilerplate, format, args...);
272 //  }
273 //
SubstituteAndAppend(std::string * output,absl::string_view format)274 inline void SubstituteAndAppend(std::string* output, absl::string_view format) {
275   substitute_internal::SubstituteAndAppendArray(output, format, nullptr, 0);
276 }
277 
SubstituteAndAppend(std::string * output,absl::string_view format,const substitute_internal::Arg & a0)278 inline void SubstituteAndAppend(std::string* output, absl::string_view format,
279                                 const substitute_internal::Arg& a0) {
280   const absl::string_view args[] = {a0.piece()};
281   substitute_internal::SubstituteAndAppendArray(output, format, args,
282                                                 ABSL_ARRAYSIZE(args));
283 }
284 
SubstituteAndAppend(std::string * output,absl::string_view format,const substitute_internal::Arg & a0,const substitute_internal::Arg & a1)285 inline void SubstituteAndAppend(std::string* output, absl::string_view format,
286                                 const substitute_internal::Arg& a0,
287                                 const substitute_internal::Arg& a1) {
288   const absl::string_view args[] = {a0.piece(), a1.piece()};
289   substitute_internal::SubstituteAndAppendArray(output, format, args,
290                                                 ABSL_ARRAYSIZE(args));
291 }
292 
SubstituteAndAppend(std::string * output,absl::string_view format,const substitute_internal::Arg & a0,const substitute_internal::Arg & a1,const substitute_internal::Arg & a2)293 inline void SubstituteAndAppend(std::string* output, absl::string_view format,
294                                 const substitute_internal::Arg& a0,
295                                 const substitute_internal::Arg& a1,
296                                 const substitute_internal::Arg& a2) {
297   const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece()};
298   substitute_internal::SubstituteAndAppendArray(output, format, args,
299                                                 ABSL_ARRAYSIZE(args));
300 }
301 
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)302 inline void SubstituteAndAppend(std::string* output, absl::string_view format,
303                                 const substitute_internal::Arg& a0,
304                                 const substitute_internal::Arg& a1,
305                                 const substitute_internal::Arg& a2,
306                                 const substitute_internal::Arg& a3) {
307   const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
308                                     a3.piece()};
309   substitute_internal::SubstituteAndAppendArray(output, format, args,
310                                                 ABSL_ARRAYSIZE(args));
311 }
312 
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)313 inline void SubstituteAndAppend(std::string* output, absl::string_view format,
314                                 const substitute_internal::Arg& a0,
315                                 const substitute_internal::Arg& a1,
316                                 const substitute_internal::Arg& a2,
317                                 const substitute_internal::Arg& a3,
318                                 const substitute_internal::Arg& a4) {
319   const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
320                                     a3.piece(), a4.piece()};
321   substitute_internal::SubstituteAndAppendArray(output, format, args,
322                                                 ABSL_ARRAYSIZE(args));
323 }
324 
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)325 inline void SubstituteAndAppend(std::string* output, absl::string_view format,
326                                 const substitute_internal::Arg& a0,
327                                 const substitute_internal::Arg& a1,
328                                 const substitute_internal::Arg& a2,
329                                 const substitute_internal::Arg& a3,
330                                 const substitute_internal::Arg& a4,
331                                 const substitute_internal::Arg& a5) {
332   const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
333                                     a3.piece(), a4.piece(), a5.piece()};
334   substitute_internal::SubstituteAndAppendArray(output, format, args,
335                                                 ABSL_ARRAYSIZE(args));
336 }
337 
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)338 inline void SubstituteAndAppend(std::string* output, absl::string_view format,
339                                 const substitute_internal::Arg& a0,
340                                 const substitute_internal::Arg& a1,
341                                 const substitute_internal::Arg& a2,
342                                 const substitute_internal::Arg& a3,
343                                 const substitute_internal::Arg& a4,
344                                 const substitute_internal::Arg& a5,
345                                 const substitute_internal::Arg& a6) {
346   const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
347                                     a3.piece(), a4.piece(), a5.piece(),
348                                     a6.piece()};
349   substitute_internal::SubstituteAndAppendArray(output, format, args,
350                                                 ABSL_ARRAYSIZE(args));
351 }
352 
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)353 inline void SubstituteAndAppend(
354     std::string* output, absl::string_view format,
355     const substitute_internal::Arg& a0, const substitute_internal::Arg& a1,
356     const substitute_internal::Arg& a2, const substitute_internal::Arg& a3,
357     const substitute_internal::Arg& a4, const substitute_internal::Arg& a5,
358     const substitute_internal::Arg& a6, const substitute_internal::Arg& a7) {
359   const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
360                                     a3.piece(), a4.piece(), a5.piece(),
361                                     a6.piece(), a7.piece()};
362   substitute_internal::SubstituteAndAppendArray(output, format, args,
363                                                 ABSL_ARRAYSIZE(args));
364 }
365 
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)366 inline void SubstituteAndAppend(
367     std::string* output, absl::string_view format,
368     const substitute_internal::Arg& a0, const substitute_internal::Arg& a1,
369     const substitute_internal::Arg& a2, const substitute_internal::Arg& a3,
370     const substitute_internal::Arg& a4, const substitute_internal::Arg& a5,
371     const substitute_internal::Arg& a6, const substitute_internal::Arg& a7,
372     const substitute_internal::Arg& a8) {
373   const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
374                                     a3.piece(), a4.piece(), a5.piece(),
375                                     a6.piece(), a7.piece(), a8.piece()};
376   substitute_internal::SubstituteAndAppendArray(output, format, args,
377                                                 ABSL_ARRAYSIZE(args));
378 }
379 
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)380 inline void SubstituteAndAppend(
381     std::string* output, absl::string_view format,
382     const substitute_internal::Arg& a0, const substitute_internal::Arg& a1,
383     const substitute_internal::Arg& a2, const substitute_internal::Arg& a3,
384     const substitute_internal::Arg& a4, const substitute_internal::Arg& a5,
385     const substitute_internal::Arg& a6, const substitute_internal::Arg& a7,
386     const substitute_internal::Arg& a8, const substitute_internal::Arg& a9) {
387   const absl::string_view args[] = {
388       a0.piece(), a1.piece(), a2.piece(), a3.piece(), a4.piece(),
389       a5.piece(), a6.piece(), a7.piece(), a8.piece(), a9.piece()};
390   substitute_internal::SubstituteAndAppendArray(output, format, args,
391                                                 ABSL_ARRAYSIZE(args));
392 }
393 
394 #if defined(ABSL_BAD_CALL_IF)
395 // This body of functions catches cases where the number of placeholders
396 // doesn't match the number of data arguments.
397 void SubstituteAndAppend(std::string* output, const char* format)
398     ABSL_BAD_CALL_IF(
399         substitute_internal::PlaceholderBitmask(format) != 0,
400         "There were no substitution arguments "
401         "but this format string either has a $[0-9] in it or contains "
402         "an unescaped $ character (use $$ instead)");
403 
404 void SubstituteAndAppend(std::string* output, const char* format,
405                          const substitute_internal::Arg& a0)
406     ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 1,
407                      "There was 1 substitution argument given, but "
408                      "this format string is missing its $0, contains "
409                      "one of $1-$9, or contains an unescaped $ character (use "
410                      "$$ instead)");
411 
412 void SubstituteAndAppend(std::string* output, const char* format,
413                          const substitute_internal::Arg& a0,
414                          const substitute_internal::Arg& a1)
415     ABSL_BAD_CALL_IF(
416         substitute_internal::PlaceholderBitmask(format) != 3,
417         "There were 2 substitution arguments given, but this format string is "
418         "missing its $0/$1, contains one of $2-$9, or contains an "
419         "unescaped $ character (use $$ instead)");
420 
421 void SubstituteAndAppend(std::string* output, const char* format,
422                          const substitute_internal::Arg& a0,
423                          const substitute_internal::Arg& a1,
424                          const substitute_internal::Arg& a2)
425     ABSL_BAD_CALL_IF(
426         substitute_internal::PlaceholderBitmask(format) != 7,
427         "There were 3 substitution arguments given, but "
428         "this format string is missing its $0/$1/$2, contains one of "
429         "$3-$9, or contains an unescaped $ character (use $$ instead)");
430 
431 void SubstituteAndAppend(std::string* output, const char* format,
432                          const substitute_internal::Arg& a0,
433                          const substitute_internal::Arg& a1,
434                          const substitute_internal::Arg& a2,
435                          const substitute_internal::Arg& a3)
436     ABSL_BAD_CALL_IF(
437         substitute_internal::PlaceholderBitmask(format) != 15,
438         "There were 4 substitution arguments given, but "
439         "this format string is missing its $0-$3, contains one of "
440         "$4-$9, or contains an unescaped $ character (use $$ instead)");
441 
442 void SubstituteAndAppend(std::string* output, const char* format,
443                          const substitute_internal::Arg& a0,
444                          const substitute_internal::Arg& a1,
445                          const substitute_internal::Arg& a2,
446                          const substitute_internal::Arg& a3,
447                          const substitute_internal::Arg& a4)
448     ABSL_BAD_CALL_IF(
449         substitute_internal::PlaceholderBitmask(format) != 31,
450         "There were 5 substitution arguments given, but "
451         "this format string is missing its $0-$4, contains one of "
452         "$5-$9, or contains an unescaped $ character (use $$ instead)");
453 
454 void SubstituteAndAppend(std::string* output, const char* format,
455                          const substitute_internal::Arg& a0,
456                          const substitute_internal::Arg& a1,
457                          const substitute_internal::Arg& a2,
458                          const substitute_internal::Arg& a3,
459                          const substitute_internal::Arg& a4,
460                          const substitute_internal::Arg& a5)
461     ABSL_BAD_CALL_IF(
462         substitute_internal::PlaceholderBitmask(format) != 63,
463         "There were 6 substitution arguments given, but "
464         "this format string is missing its $0-$5, contains one of "
465         "$6-$9, or contains an unescaped $ character (use $$ instead)");
466 
467 void SubstituteAndAppend(
468     std::string* output, const char* format, const substitute_internal::Arg& a0,
469     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
470     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
471     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6)
472     ABSL_BAD_CALL_IF(
473         substitute_internal::PlaceholderBitmask(format) != 127,
474         "There were 7 substitution arguments given, but "
475         "this format string is missing its $0-$6, contains one of "
476         "$7-$9, or contains an unescaped $ character (use $$ instead)");
477 
478 void SubstituteAndAppend(
479     std::string* output, const char* format, const substitute_internal::Arg& a0,
480     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
481     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
482     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
483     const substitute_internal::Arg& a7)
484     ABSL_BAD_CALL_IF(
485         substitute_internal::PlaceholderBitmask(format) != 255,
486         "There were 8 substitution arguments given, but "
487         "this format string is missing its $0-$7, contains one of "
488         "$8-$9, or contains an unescaped $ character (use $$ instead)");
489 
490 void SubstituteAndAppend(
491     std::string* output, const char* format, const substitute_internal::Arg& a0,
492     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
493     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
494     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
495     const substitute_internal::Arg& a7, const substitute_internal::Arg& a8)
496     ABSL_BAD_CALL_IF(
497         substitute_internal::PlaceholderBitmask(format) != 511,
498         "There were 9 substitution arguments given, but "
499         "this format string is missing its $0-$8, contains a $9, or "
500         "contains an unescaped $ character (use $$ instead)");
501 
502 void SubstituteAndAppend(
503     std::string* output, const char* format, const substitute_internal::Arg& a0,
504     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
505     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
506     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
507     const substitute_internal::Arg& a7, const substitute_internal::Arg& a8,
508     const substitute_internal::Arg& a9)
509     ABSL_BAD_CALL_IF(
510         substitute_internal::PlaceholderBitmask(format) != 1023,
511         "There were 10 substitution arguments given, but this "
512         "format string either doesn't contain all of $0 through $9 or "
513         "contains an unescaped $ character (use $$ instead)");
514 #endif  // ABSL_BAD_CALL_IF
515 
516 // Substitute()
517 //
518 // Substitutes variables into a given format string. See file comments above
519 // for usage.
520 //
521 // The declarations of `Substitute()` below consist of overloads for passing 0
522 // to 10 arguments, respectively.
523 //
524 // NOTE: A zero-argument `Substitute()` may be used within variadic templates to
525 // allow a variable number of arguments.
526 //
527 // Example:
528 //  template <typename... Args>
529 //  void VarMsg(absl::string_view format, const Args&... args) {
530 //    std::string s = absl::Substitute(format, args...);
531 
Substitute(absl::string_view format)532 ABSL_MUST_USE_RESULT inline std::string Substitute(absl::string_view format) {
533   std::string result;
534   SubstituteAndAppend(&result, format);
535   return result;
536 }
537 
Substitute(absl::string_view format,const substitute_internal::Arg & a0)538 ABSL_MUST_USE_RESULT inline std::string Substitute(
539     absl::string_view format, const substitute_internal::Arg& a0) {
540   std::string result;
541   SubstituteAndAppend(&result, format, a0);
542   return result;
543 }
544 
Substitute(absl::string_view format,const substitute_internal::Arg & a0,const substitute_internal::Arg & a1)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) {
548   std::string result;
549   SubstituteAndAppend(&result, format, a0, a1);
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)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   std::string result;
557   SubstituteAndAppend(&result, format, a0, a1, a2);
558   return result;
559 }
560 
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)561 ABSL_MUST_USE_RESULT inline std::string Substitute(
562     absl::string_view format, const substitute_internal::Arg& a0,
563     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
564     const substitute_internal::Arg& a3) {
565   std::string result;
566   SubstituteAndAppend(&result, format, a0, a1, a2, a3);
567   return result;
568 }
569 
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)570 ABSL_MUST_USE_RESULT inline std::string Substitute(
571     absl::string_view format, const substitute_internal::Arg& a0,
572     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
573     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4) {
574   std::string result;
575   SubstituteAndAppend(&result, format, a0, a1, a2, a3, a4);
576   return result;
577 }
578 
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)579 ABSL_MUST_USE_RESULT inline std::string Substitute(
580     absl::string_view format, const substitute_internal::Arg& a0,
581     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
582     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
583     const substitute_internal::Arg& a5) {
584   std::string result;
585   SubstituteAndAppend(&result, format, a0, a1, a2, a3, a4, a5);
586   return result;
587 }
588 
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)589 ABSL_MUST_USE_RESULT inline std::string Substitute(
590     absl::string_view format, const substitute_internal::Arg& a0,
591     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
592     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
593     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6) {
594   std::string result;
595   SubstituteAndAppend(&result, format, a0, a1, a2, a3, a4, a5, a6);
596   return result;
597 }
598 
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)599 ABSL_MUST_USE_RESULT inline std::string Substitute(
600     absl::string_view format, const substitute_internal::Arg& a0,
601     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
602     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
603     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
604     const substitute_internal::Arg& a7) {
605   std::string result;
606   SubstituteAndAppend(&result, format, a0, a1, a2, a3, a4, a5, a6, a7);
607   return result;
608 }
609 
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)610 ABSL_MUST_USE_RESULT inline std::string Substitute(
611     absl::string_view format, const substitute_internal::Arg& a0,
612     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
613     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
614     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
615     const substitute_internal::Arg& a7, const substitute_internal::Arg& a8) {
616   std::string result;
617   SubstituteAndAppend(&result, format, a0, a1, a2, a3, a4, a5, a6, a7, a8);
618   return result;
619 }
620 
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)621 ABSL_MUST_USE_RESULT inline std::string Substitute(
622     absl::string_view format, const substitute_internal::Arg& a0,
623     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
624     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
625     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
626     const substitute_internal::Arg& a7, const substitute_internal::Arg& a8,
627     const substitute_internal::Arg& a9) {
628   std::string result;
629   SubstituteAndAppend(&result, format, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
630   return result;
631 }
632 
633 #if defined(ABSL_BAD_CALL_IF)
634 // This body of functions catches cases where the number of placeholders
635 // doesn't match the number of data arguments.
636 std::string Substitute(const char* format)
637     ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 0,
638                      "There were no substitution arguments "
639                      "but this format string either has a $[0-9] in it or "
640                      "contains an unescaped $ character (use $$ instead)");
641 
642 std::string Substitute(const char* format, const substitute_internal::Arg& a0)
643     ABSL_BAD_CALL_IF(
644         substitute_internal::PlaceholderBitmask(format) != 1,
645         "There was 1 substitution argument given, but "
646         "this format string is missing its $0, contains one of $1-$9, "
647         "or contains an unescaped $ character (use $$ instead)");
648 
649 std::string Substitute(const char* format, const substitute_internal::Arg& a0,
650                        const substitute_internal::Arg& a1)
651     ABSL_BAD_CALL_IF(
652         substitute_internal::PlaceholderBitmask(format) != 3,
653         "There were 2 substitution arguments given, but "
654         "this format string is missing its $0/$1, contains one of "
655         "$2-$9, or contains an unescaped $ character (use $$ instead)");
656 
657 std::string Substitute(const char* format, const substitute_internal::Arg& a0,
658                        const substitute_internal::Arg& a1,
659                        const substitute_internal::Arg& a2)
660     ABSL_BAD_CALL_IF(
661         substitute_internal::PlaceholderBitmask(format) != 7,
662         "There were 3 substitution arguments given, but "
663         "this format string is missing its $0/$1/$2, contains one of "
664         "$3-$9, or contains an unescaped $ character (use $$ instead)");
665 
666 std::string Substitute(const char* format, const substitute_internal::Arg& a0,
667                        const substitute_internal::Arg& a1,
668                        const substitute_internal::Arg& a2,
669                        const substitute_internal::Arg& a3)
670     ABSL_BAD_CALL_IF(
671         substitute_internal::PlaceholderBitmask(format) != 15,
672         "There were 4 substitution arguments given, but "
673         "this format string is missing its $0-$3, contains one of "
674         "$4-$9, or contains an unescaped $ character (use $$ instead)");
675 
676 std::string Substitute(const char* format, const substitute_internal::Arg& a0,
677                        const substitute_internal::Arg& a1,
678                        const substitute_internal::Arg& a2,
679                        const substitute_internal::Arg& a3,
680                        const substitute_internal::Arg& a4)
681     ABSL_BAD_CALL_IF(
682         substitute_internal::PlaceholderBitmask(format) != 31,
683         "There were 5 substitution arguments given, but "
684         "this format string is missing its $0-$4, contains one of "
685         "$5-$9, or contains an unescaped $ character (use $$ instead)");
686 
687 std::string Substitute(const char* format, const substitute_internal::Arg& a0,
688                        const substitute_internal::Arg& a1,
689                        const substitute_internal::Arg& a2,
690                        const substitute_internal::Arg& a3,
691                        const substitute_internal::Arg& a4,
692                        const substitute_internal::Arg& a5)
693     ABSL_BAD_CALL_IF(
694         substitute_internal::PlaceholderBitmask(format) != 63,
695         "There were 6 substitution arguments given, but "
696         "this format string is missing its $0-$5, contains one of "
697         "$6-$9, or contains an unescaped $ character (use $$ instead)");
698 
699 std::string Substitute(const char* format, const substitute_internal::Arg& a0,
700                        const substitute_internal::Arg& a1,
701                        const substitute_internal::Arg& a2,
702                        const substitute_internal::Arg& a3,
703                        const substitute_internal::Arg& a4,
704                        const substitute_internal::Arg& a5,
705                        const substitute_internal::Arg& a6)
706     ABSL_BAD_CALL_IF(
707         substitute_internal::PlaceholderBitmask(format) != 127,
708         "There were 7 substitution arguments given, but "
709         "this format string is missing its $0-$6, contains one of "
710         "$7-$9, or contains an unescaped $ character (use $$ instead)");
711 
712 std::string Substitute(const char* format, const substitute_internal::Arg& a0,
713                        const substitute_internal::Arg& a1,
714                        const substitute_internal::Arg& a2,
715                        const substitute_internal::Arg& a3,
716                        const substitute_internal::Arg& a4,
717                        const substitute_internal::Arg& a5,
718                        const substitute_internal::Arg& a6,
719                        const substitute_internal::Arg& a7)
720     ABSL_BAD_CALL_IF(
721         substitute_internal::PlaceholderBitmask(format) != 255,
722         "There were 8 substitution arguments given, but "
723         "this format string is missing its $0-$7, contains one of "
724         "$8-$9, or contains an unescaped $ character (use $$ instead)");
725 
726 std::string Substitute(
727     const char* format, const substitute_internal::Arg& a0,
728     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
729     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
730     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
731     const substitute_internal::Arg& a7, const substitute_internal::Arg& a8)
732     ABSL_BAD_CALL_IF(
733         substitute_internal::PlaceholderBitmask(format) != 511,
734         "There were 9 substitution arguments given, but "
735         "this format string is missing its $0-$8, contains a $9, or "
736         "contains an unescaped $ character (use $$ instead)");
737 
738 std::string Substitute(
739     const char* format, const substitute_internal::Arg& a0,
740     const substitute_internal::Arg& a1, const substitute_internal::Arg& a2,
741     const substitute_internal::Arg& a3, const substitute_internal::Arg& a4,
742     const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
743     const substitute_internal::Arg& a7, const substitute_internal::Arg& a8,
744     const substitute_internal::Arg& a9)
745     ABSL_BAD_CALL_IF(
746         substitute_internal::PlaceholderBitmask(format) != 1023,
747         "There were 10 substitution arguments given, but this "
748         "format string either doesn't contain all of $0 through $9 or "
749         "contains an unescaped $ character (use $$ instead)");
750 #endif  // ABSL_BAD_CALL_IF
751 
752 ABSL_NAMESPACE_END
753 }  // namespace absl
754 
755 #endif  // ABSL_STRINGS_SUBSTITUTE_H_
756