format-inl.h 22 KB

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