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