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