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