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