substitute.h 35 KB

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