format-inl.h 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768
  1. #ifndef FORMAT_INL_H_
  2. #error "Direct inclusion of this file is not allowed, include format.h"
  3. // For the sake of sane code completion.
  4. #include "format.h"
  5. #endif
  6. #include "enum.h"
  7. #include "string.h"
  8. #include <library/cpp/yt/assert/assert.h>
  9. #include <library/cpp/yt/small_containers/compact_vector.h>
  10. #include <library/cpp/yt/containers/enum_indexed_array.h>
  11. #include <library/cpp/yt/misc/enum.h>
  12. #include <util/system/platform.h>
  13. #include <cctype>
  14. #include <optional>
  15. namespace NYT {
  16. ////////////////////////////////////////////////////////////////////////////////
  17. static constexpr char GenericSpecSymbol = 'v';
  18. inline bool IsQuotationSpecSymbol(char symbol)
  19. {
  20. return symbol == 'Q' || symbol == 'q';
  21. }
  22. // TStringBuf
  23. inline void FormatValue(TStringBuilderBase* builder, TStringBuf value, TStringBuf format)
  24. {
  25. if (!format) {
  26. builder->AppendString(value);
  27. return;
  28. }
  29. // Parse alignment.
  30. bool alignLeft = false;
  31. const char* current = format.begin();
  32. if (*current == '-') {
  33. alignLeft = true;
  34. ++current;
  35. }
  36. bool hasAlign = false;
  37. int alignSize = 0;
  38. while (*current >= '0' && *current <= '9') {
  39. hasAlign = true;
  40. alignSize = 10 * alignSize + (*current - '0');
  41. if (alignSize > 1000000) {
  42. builder->AppendString(TStringBuf("<alignment overflow>"));
  43. return;
  44. }
  45. ++current;
  46. }
  47. int padding = 0;
  48. bool padLeft = false;
  49. bool padRight = false;
  50. if (hasAlign) {
  51. padding = alignSize - value.size();
  52. if (padding < 0) {
  53. padding = 0;
  54. }
  55. padLeft = !alignLeft;
  56. padRight = alignLeft;
  57. }
  58. bool singleQuotes = false;
  59. bool doubleQuotes = false;
  60. bool escape = false;
  61. while (current < format.end()) {
  62. switch (*current++) {
  63. case 'q':
  64. singleQuotes = true;
  65. break;
  66. case 'Q':
  67. doubleQuotes = true;
  68. break;
  69. case 'h':
  70. escape = true;
  71. break;
  72. }
  73. }
  74. if (padLeft) {
  75. builder->AppendChar(' ', padding);
  76. }
  77. if (singleQuotes || doubleQuotes || escape) {
  78. for (const char* valueCurrent = value.begin(); valueCurrent < value.end(); ++valueCurrent) {
  79. char ch = *valueCurrent;
  80. if (ch == '\n') {
  81. builder->AppendString("\\n");
  82. } else if (ch == '\t') {
  83. builder->AppendString("\\t");
  84. } else if (ch == '\\') {
  85. builder->AppendString("\\\\");
  86. } else if (ch < PrintableASCIILow || ch > PrintableASCIIHigh) {
  87. builder->AppendString("\\x");
  88. builder->AppendChar(IntToHexLowercase[static_cast<ui8>(ch) >> 4]);
  89. builder->AppendChar(IntToHexLowercase[static_cast<ui8>(ch) & 0xf]);
  90. } else if ((singleQuotes && ch == '\'') || (doubleQuotes && ch == '\"')) {
  91. builder->AppendChar('\\');
  92. builder->AppendChar(ch);
  93. } else {
  94. builder->AppendChar(ch);
  95. }
  96. }
  97. } else {
  98. builder->AppendString(value);
  99. }
  100. if (padRight) {
  101. builder->AppendChar(' ', padding);
  102. }
  103. }
  104. // TString
  105. inline void FormatValue(TStringBuilderBase* builder, const TString& value, TStringBuf format)
  106. {
  107. FormatValue(builder, TStringBuf(value), format);
  108. }
  109. // const char*
  110. inline void FormatValue(TStringBuilderBase* builder, const char* value, TStringBuf format)
  111. {
  112. FormatValue(builder, TStringBuf(value), format);
  113. }
  114. // char*
  115. inline void FormatValue(TStringBuilderBase* builder, char* value, TStringBuf format)
  116. {
  117. FormatValue(builder, TStringBuf(value), format);
  118. }
  119. // char
  120. inline void FormatValue(TStringBuilderBase* builder, char value, TStringBuf format)
  121. {
  122. FormatValue(builder, TStringBuf(&value, 1), format);
  123. }
  124. // bool
  125. inline void FormatValue(TStringBuilderBase* builder, bool value, TStringBuf format)
  126. {
  127. // Parse custom flags.
  128. bool lowercase = false;
  129. const char* current = format.begin();
  130. while (current != format.end()) {
  131. if (*current == 'l') {
  132. ++current;
  133. lowercase = true;
  134. } else if (IsQuotationSpecSymbol(*current)) {
  135. ++current;
  136. } else
  137. break;
  138. }
  139. auto str = lowercase
  140. ? (value ? TStringBuf("true") : TStringBuf("false"))
  141. : (value ? TStringBuf("True") : TStringBuf("False"));
  142. builder->AppendString(str);
  143. }
  144. // Fallback to ToString
  145. struct TToStringFallbackValueFormatterTag
  146. { };
  147. template <class TValue, class = void>
  148. struct TValueFormatter
  149. {
  150. static TToStringFallbackValueFormatterTag Do(TStringBuilderBase* builder, const TValue& value, TStringBuf format)
  151. {
  152. using ::ToString;
  153. FormatValue(builder, ToString(value), format);
  154. return {};
  155. }
  156. };
  157. // Enum
  158. template <class TEnum>
  159. struct TValueFormatter<TEnum, typename std::enable_if<TEnumTraits<TEnum>::IsEnum>::type>
  160. {
  161. static void Do(TStringBuilderBase* builder, TEnum value, TStringBuf format)
  162. {
  163. // Parse custom flags.
  164. bool lowercase = false;
  165. const char* current = format.begin();
  166. while (current != format.end()) {
  167. if (*current == 'l') {
  168. ++current;
  169. lowercase = true;
  170. } else if (IsQuotationSpecSymbol(*current)) {
  171. ++current;
  172. } else {
  173. break;
  174. }
  175. }
  176. FormatEnum(builder, value, lowercase);
  177. }
  178. };
  179. template <class TRange, class TFormatter>
  180. typename TFormattableView<TRange, TFormatter>::TBegin TFormattableView<TRange, TFormatter>::begin() const
  181. {
  182. return RangeBegin;
  183. }
  184. template <class TRange, class TFormatter>
  185. typename TFormattableView<TRange, TFormatter>::TEnd TFormattableView<TRange, TFormatter>::end() const
  186. {
  187. return RangeEnd;
  188. }
  189. template <class TRange, class TFormatter>
  190. TFormattableView<TRange, TFormatter> MakeFormattableView(
  191. const TRange& range,
  192. TFormatter&& formatter)
  193. {
  194. return TFormattableView<TRange, std::decay_t<TFormatter>>{range.begin(), range.end(), std::forward<TFormatter>(formatter)};
  195. }
  196. template <class TRange, class TFormatter>
  197. TFormattableView<TRange, TFormatter> MakeShrunkFormattableView(
  198. const TRange& range,
  199. TFormatter&& formatter,
  200. size_t limit)
  201. {
  202. return TFormattableView<TRange, std::decay_t<TFormatter>>{range.begin(), range.end(), std::forward<TFormatter>(formatter), limit};
  203. }
  204. template <class TRange, class TFormatter>
  205. void FormatRange(TStringBuilderBase* builder, const TRange& range, const TFormatter& formatter, size_t limit = std::numeric_limits<size_t>::max())
  206. {
  207. builder->AppendChar('[');
  208. size_t index = 0;
  209. for (const auto& item : range) {
  210. if (index > 0) {
  211. builder->AppendString(DefaultJoinToStringDelimiter);
  212. }
  213. if (index == limit) {
  214. builder->AppendString(DefaultRangeEllipsisFormat);
  215. break;
  216. }
  217. formatter(builder, item);
  218. ++index;
  219. }
  220. builder->AppendChar(']');
  221. }
  222. template <class TRange, class TFormatter>
  223. void FormatKeyValueRange(TStringBuilderBase* builder, const TRange& range, const TFormatter& formatter, size_t limit = std::numeric_limits<size_t>::max())
  224. {
  225. builder->AppendChar('{');
  226. size_t index = 0;
  227. for (const auto& item : range) {
  228. if (index > 0) {
  229. builder->AppendString(DefaultJoinToStringDelimiter);
  230. }
  231. if (index == limit) {
  232. builder->AppendString(DefaultRangeEllipsisFormat);
  233. break;
  234. }
  235. formatter(builder, item.first);
  236. builder->AppendString(DefaultKeyValueDelimiter);
  237. formatter(builder, item.second);
  238. ++index;
  239. }
  240. builder->AppendChar('}');
  241. }
  242. // TFormattableView
  243. template <class TRange, class TFormatter>
  244. struct TValueFormatter<TFormattableView<TRange, TFormatter>>
  245. {
  246. static void Do(TStringBuilderBase* builder, const TFormattableView<TRange, TFormatter>& range, TStringBuf /*format*/)
  247. {
  248. FormatRange(builder, range, range.Formatter, range.Limit);
  249. }
  250. };
  251. template <class TFormatter>
  252. TFormatterWrapper<TFormatter> MakeFormatterWrapper(
  253. TFormatter&& formatter)
  254. {
  255. return TFormatterWrapper<TFormatter>{
  256. .Formatter = std::move(formatter)
  257. };
  258. }
  259. // TFormatterWrapper
  260. template <class TFormatter>
  261. struct TValueFormatter<TFormatterWrapper<TFormatter>>
  262. {
  263. static void Do(TStringBuilderBase* builder, const TFormatterWrapper<TFormatter>& wrapper, TStringBuf /*format*/)
  264. {
  265. wrapper.Formatter(builder);
  266. }
  267. };
  268. // std::vector
  269. template <class T, class TAllocator>
  270. struct TValueFormatter<std::vector<T, TAllocator>>
  271. {
  272. static void Do(TStringBuilderBase* builder, const std::vector<T, TAllocator>& collection, TStringBuf /*format*/)
  273. {
  274. FormatRange(builder, collection, TDefaultFormatter());
  275. }
  276. };
  277. // TCompactVector
  278. template <class T, unsigned N>
  279. struct TValueFormatter<TCompactVector<T, N>>
  280. {
  281. static void Do(TStringBuilderBase* builder, const TCompactVector<T, N>& collection, TStringBuf /*format*/)
  282. {
  283. FormatRange(builder, collection, TDefaultFormatter());
  284. }
  285. };
  286. // std::set
  287. template <class T>
  288. struct TValueFormatter<std::set<T>>
  289. {
  290. static void Do(TStringBuilderBase* builder, const std::set<T>& collection, TStringBuf /*format*/)
  291. {
  292. FormatRange(builder, collection, TDefaultFormatter());
  293. }
  294. };
  295. // std::map
  296. template <class K, class V>
  297. struct TValueFormatter<std::map<K, V>>
  298. {
  299. static void Do(TStringBuilderBase* builder, const std::map<K, V>& collection, TStringBuf /*format*/)
  300. {
  301. FormatKeyValueRange(builder, collection, TDefaultFormatter());
  302. }
  303. };
  304. // std::multimap
  305. template <class K, class V>
  306. struct TValueFormatter<std::multimap<K, V>>
  307. {
  308. static void Do(TStringBuilderBase* builder, const std::multimap<K, V>& collection, TStringBuf /*format*/)
  309. {
  310. FormatKeyValueRange(builder, collection, TDefaultFormatter());
  311. }
  312. };
  313. // THashSet
  314. template <class T>
  315. struct TValueFormatter<THashSet<T>>
  316. {
  317. static void Do(TStringBuilderBase* builder, const THashSet<T>& collection, TStringBuf /*format*/)
  318. {
  319. FormatRange(builder, collection, TDefaultFormatter());
  320. }
  321. };
  322. // THashMultiSet
  323. template <class T>
  324. struct TValueFormatter<THashMultiSet<T>>
  325. {
  326. static void Do(TStringBuilderBase* builder, const THashMultiSet<T>& collection, TStringBuf /*format*/)
  327. {
  328. FormatRange(builder, collection, TDefaultFormatter());
  329. }
  330. };
  331. // THashMap
  332. template <class K, class V>
  333. struct TValueFormatter<THashMap<K, V>>
  334. {
  335. static void Do(TStringBuilderBase* builder, const THashMap<K, V>& collection, TStringBuf /*format*/)
  336. {
  337. FormatKeyValueRange(builder, collection, TDefaultFormatter());
  338. }
  339. };
  340. // THashMultiMap
  341. template <class K, class V>
  342. struct TValueFormatter<THashMultiMap<K, V>>
  343. {
  344. static void Do(TStringBuilderBase* builder, const THashMultiMap<K, V>& collection, TStringBuf /*format*/)
  345. {
  346. FormatKeyValueRange(builder, collection, TDefaultFormatter());
  347. }
  348. };
  349. // TEnumIndexedArray
  350. template <class E, class T>
  351. struct TValueFormatter<TEnumIndexedArray<E, T>>
  352. {
  353. static void Do(TStringBuilderBase* builder, const TEnumIndexedArray<E, T>& collection, TStringBuf format)
  354. {
  355. builder->AppendChar('{');
  356. bool firstItem = true;
  357. for (const auto& index : TEnumTraits<E>::GetDomainValues()) {
  358. if (!firstItem) {
  359. builder->AppendString(DefaultJoinToStringDelimiter);
  360. }
  361. FormatValue(builder, index, format);
  362. builder->AppendString(": ");
  363. FormatValue(builder, collection[index], format);
  364. firstItem = false;
  365. }
  366. builder->AppendChar('}');
  367. }
  368. };
  369. // std::pair
  370. template <class T1, class T2>
  371. struct TValueFormatter<std::pair<T1, T2>>
  372. {
  373. static void Do(TStringBuilderBase* builder, const std::pair<T1, T2>& value, TStringBuf format)
  374. {
  375. builder->AppendChar('{');
  376. FormatValue(builder, value.first, format);
  377. builder->AppendString(TStringBuf(", "));
  378. FormatValue(builder, value.second, format);
  379. builder->AppendChar('}');
  380. }
  381. };
  382. // std::optional
  383. inline void FormatValue(TStringBuilderBase* builder, std::nullopt_t, TStringBuf /*format*/)
  384. {
  385. builder->AppendString(TStringBuf("<null>"));
  386. }
  387. template <class T>
  388. struct TValueFormatter<std::optional<T>>
  389. {
  390. static void Do(TStringBuilderBase* builder, const std::optional<T>& value, TStringBuf format)
  391. {
  392. if (value) {
  393. FormatValue(builder, *value, format);
  394. } else {
  395. FormatValue(builder, std::nullopt, format);
  396. }
  397. }
  398. };
  399. template <class TValue>
  400. auto FormatValue(TStringBuilderBase* builder, const TValue& value, TStringBuf format) ->
  401. decltype(TValueFormatter<TValue>::Do(builder, value, format))
  402. {
  403. return TValueFormatter<TValue>::Do(builder, value, format);
  404. }
  405. namespace NDetail {
  406. template <class TValue>
  407. void FormatValueViaSprintf(
  408. TStringBuilderBase* builder,
  409. TValue value,
  410. TStringBuf format,
  411. TStringBuf genericSpec);
  412. template <class TValue>
  413. void FormatIntValue(
  414. TStringBuilderBase* builder,
  415. TValue value,
  416. TStringBuf format,
  417. TStringBuf genericSpec);
  418. void FormatPointerValue(
  419. TStringBuilderBase* builder,
  420. const void* value,
  421. TStringBuf format);
  422. } // namespace NDetail
  423. #define XX(valueType, castType, genericSpec) \
  424. inline void FormatValue(TStringBuilderBase* builder, valueType value, TStringBuf format) \
  425. { \
  426. NYT::NDetail::FormatIntValue(builder, static_cast<castType>(value), format, genericSpec); \
  427. }
  428. XX(i8, i32, TStringBuf("d"))
  429. XX(ui8, ui32, TStringBuf("u"))
  430. XX(i16, i32, TStringBuf("d"))
  431. XX(ui16, ui32, TStringBuf("u"))
  432. XX(i32, i32, TStringBuf("d"))
  433. XX(ui32, ui32, TStringBuf("u"))
  434. #ifdef _win_
  435. XX(long long, i64, TStringBuf("lld"))
  436. XX(unsigned long long, ui64, TStringBuf("llu"))
  437. #else
  438. XX(long, i64, TStringBuf("ld"))
  439. XX(unsigned long, ui64, TStringBuf("lu"))
  440. #endif
  441. #undef XX
  442. #define XX(valueType, castType, genericSpec) \
  443. inline void FormatValue(TStringBuilderBase* builder, valueType value, TStringBuf format) \
  444. { \
  445. NYT::NDetail::FormatValueViaSprintf(builder, static_cast<castType>(value), format, genericSpec); \
  446. }
  447. XX(double, double, TStringBuf("lf"))
  448. XX(float, float, TStringBuf("f"))
  449. #undef XX
  450. // Pointer
  451. template <class T>
  452. void FormatValue(TStringBuilderBase* builder, T* value, TStringBuf format)
  453. {
  454. NYT::NDetail::FormatPointerValue(builder, static_cast<const void*>(value), format);
  455. }
  456. // TDuration (specialize for performance reasons)
  457. inline void FormatValue(TStringBuilderBase* builder, TDuration value, TStringBuf /*format*/)
  458. {
  459. builder->AppendFormat("%vus", value.MicroSeconds());
  460. }
  461. // TInstant (specialize for TFormatTraits)
  462. inline void FormatValue(TStringBuilderBase* builder, TInstant value, TStringBuf format)
  463. {
  464. // TODO(babenko): optimize
  465. builder->AppendFormat("%v", ToString(value), format);
  466. }
  467. ////////////////////////////////////////////////////////////////////////////////
  468. namespace NDetail {
  469. template <class TArgFormatter>
  470. void FormatImpl(
  471. TStringBuilderBase* builder,
  472. TStringBuf format,
  473. const TArgFormatter& argFormatter)
  474. {
  475. size_t argIndex = 0;
  476. auto current = format.begin();
  477. while (true) {
  478. // Scan verbatim part until stop symbol.
  479. auto verbatimBegin = current;
  480. auto verbatimEnd = verbatimBegin;
  481. while (verbatimEnd != format.end() && *verbatimEnd != '%') {
  482. ++verbatimEnd;
  483. }
  484. // Copy verbatim part, if any.
  485. size_t verbatimSize = verbatimEnd - verbatimBegin;
  486. if (verbatimSize > 0) {
  487. builder->AppendString(TStringBuf(verbatimBegin, verbatimSize));
  488. }
  489. // Handle stop symbol.
  490. current = verbatimEnd;
  491. if (current == format.end()) {
  492. break;
  493. }
  494. YT_ASSERT(*current == '%');
  495. ++current;
  496. if (*current == '%') {
  497. // Verbatim %.
  498. builder->AppendChar('%');
  499. ++current;
  500. } else {
  501. // Scan format part until stop symbol.
  502. auto argFormatBegin = current;
  503. auto argFormatEnd = argFormatBegin;
  504. bool singleQuotes = false;
  505. bool doubleQuotes = false;
  506. while (
  507. argFormatEnd != format.end() &&
  508. *argFormatEnd != GenericSpecSymbol && // value in generic format
  509. *argFormatEnd != 'd' && // others are standard specifiers supported by printf
  510. *argFormatEnd != 'i' &&
  511. *argFormatEnd != 'u' &&
  512. *argFormatEnd != 'o' &&
  513. *argFormatEnd != 'x' &&
  514. *argFormatEnd != 'X' &&
  515. *argFormatEnd != 'f' &&
  516. *argFormatEnd != 'F' &&
  517. *argFormatEnd != 'e' &&
  518. *argFormatEnd != 'E' &&
  519. *argFormatEnd != 'g' &&
  520. *argFormatEnd != 'G' &&
  521. *argFormatEnd != 'a' &&
  522. *argFormatEnd != 'A' &&
  523. *argFormatEnd != 'c' &&
  524. *argFormatEnd != 's' &&
  525. *argFormatEnd != 'p' &&
  526. *argFormatEnd != 'n')
  527. {
  528. switch (*argFormatEnd) {
  529. case 'q':
  530. singleQuotes = true;
  531. break;
  532. case 'Q':
  533. doubleQuotes = true;
  534. break;
  535. case 'h':
  536. break;
  537. }
  538. ++argFormatEnd;
  539. }
  540. // Handle end of format string.
  541. if (argFormatEnd != format.end()) {
  542. ++argFormatEnd;
  543. }
  544. // 'n' means 'nothing'; skip the argument.
  545. if (*argFormatBegin != 'n') {
  546. // Format argument.
  547. TStringBuf argFormat(argFormatBegin, argFormatEnd);
  548. if (singleQuotes) {
  549. builder->AppendChar('\'');
  550. }
  551. if (doubleQuotes) {
  552. builder->AppendChar('"');
  553. }
  554. argFormatter(argIndex++, builder, argFormat);
  555. if (singleQuotes) {
  556. builder->AppendChar('\'');
  557. }
  558. if (doubleQuotes) {
  559. builder->AppendChar('"');
  560. }
  561. }
  562. current = argFormatEnd;
  563. }
  564. }
  565. }
  566. } // namespace NDetail
  567. ////////////////////////////////////////////////////////////////////////////////
  568. template <class... TArgs>
  569. TLazyMultiValueFormatter<TArgs...>::TLazyMultiValueFormatter(
  570. TStringBuf format,
  571. TArgs&&... args)
  572. : Format_(format)
  573. , Args_(std::forward<TArgs>(args)...)
  574. { }
  575. template <class... TArgs>
  576. void FormatValue(
  577. TStringBuilderBase* builder,
  578. const TLazyMultiValueFormatter<TArgs...>& value,
  579. TStringBuf /*format*/)
  580. {
  581. std::apply(
  582. [&] <class... TInnerArgs> (TInnerArgs&&... args) {
  583. builder->AppendFormat(value.Format_, std::forward<TInnerArgs>(args)...);
  584. },
  585. value.Args_);
  586. }
  587. template <class... TArgs>
  588. auto MakeLazyMultiValueFormatter(TStringBuf format, TArgs&&... args)
  589. {
  590. return TLazyMultiValueFormatter<TArgs...>(format, std::forward<TArgs>(args)...);
  591. }
  592. ////////////////////////////////////////////////////////////////////////////////
  593. template <class T>
  594. struct TFormatTraits
  595. {
  596. static constexpr bool HasCustomFormatValue = !std::is_same_v<
  597. decltype(FormatValue(
  598. static_cast<TStringBuilderBase*>(nullptr),
  599. *static_cast<const T*>(nullptr),
  600. TStringBuf())),
  601. TToStringFallbackValueFormatterTag>;
  602. };
  603. ////////////////////////////////////////////////////////////////////////////////
  604. template <size_t IndexBase, class... TArgs>
  605. struct TArgFormatterImpl;
  606. template <size_t IndexBase>
  607. struct TArgFormatterImpl<IndexBase>
  608. {
  609. void operator() (size_t /*index*/, TStringBuilderBase* builder, TStringBuf /*format*/) const
  610. {
  611. builder->AppendString(TStringBuf("<missing argument>"));
  612. }
  613. };
  614. template <size_t IndexBase, class THeadArg, class... TTailArgs>
  615. struct TArgFormatterImpl<IndexBase, THeadArg, TTailArgs...>
  616. {
  617. explicit TArgFormatterImpl(const THeadArg& headArg, const TTailArgs&... tailArgs)
  618. : HeadArg(headArg)
  619. , TailFormatter(tailArgs...)
  620. { }
  621. const THeadArg& HeadArg;
  622. TArgFormatterImpl<IndexBase + 1, TTailArgs...> TailFormatter;
  623. void operator() (size_t index, TStringBuilderBase* builder, TStringBuf format) const
  624. {
  625. YT_ASSERT(index >= IndexBase);
  626. if (index == IndexBase) {
  627. FormatValue(builder, HeadArg, format);
  628. } else {
  629. TailFormatter(index, builder, format);
  630. }
  631. }
  632. };
  633. ////////////////////////////////////////////////////////////////////////////////
  634. template <size_t Length, class... TArgs>
  635. void Format(
  636. TStringBuilderBase* builder,
  637. const char (&format)[Length],
  638. TArgs&&... args)
  639. {
  640. Format(builder, TStringBuf(format, Length - 1), std::forward<TArgs>(args)...);
  641. }
  642. template <class... TArgs>
  643. void Format(
  644. TStringBuilderBase* builder,
  645. TStringBuf format,
  646. TArgs&&... args)
  647. {
  648. TArgFormatterImpl<0, TArgs...> argFormatter(args...);
  649. NYT::NDetail::FormatImpl(builder, format, argFormatter);
  650. }
  651. template <size_t Length, class... TArgs>
  652. TString Format(
  653. const char (&format)[Length],
  654. TArgs&&... args)
  655. {
  656. TStringBuilder builder;
  657. Format(&builder, format, std::forward<TArgs>(args)...);
  658. return builder.Flush();
  659. }
  660. template <class... TArgs>
  661. TString Format(
  662. TStringBuf format,
  663. TArgs&&... args)
  664. {
  665. TStringBuilder builder;
  666. Format(&builder, format, std::forward<TArgs>(args)...);
  667. return builder.Flush();
  668. }
  669. ////////////////////////////////////////////////////////////////////////////////
  670. } // namespace NYT