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