substitute.h 35 KB

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