format.h 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. #pragma once
  2. #include "format_string.h"
  3. #include "string_builder.h"
  4. #include <util/generic/string.h>
  5. #include <iterator>
  6. namespace NYT {
  7. ////////////////////////////////////////////////////////////////////////////////
  8. /*
  9. * Format: a type-safe and fast formatting utility.
  10. *
  11. * Basically works as a type-safe analogue of |sprintf| and is expected to
  12. * be backwards-compatible with the latter.
  13. *
  14. * Like Go's |Sprintf|, supports the ultimate format specifier |v|
  15. * causing arguments to be emitted in default format.
  16. * This is the default and preferred way of formatting things,
  17. * which should be used in newer code.
  18. *
  19. * |Format| may currently invoke |sprintf| internally for emitting numeric and some other
  20. * types. You can always write your own optimized implementation, if you wish :)
  21. *
  22. * In additional to the usual |sprintf|, supports a number of non-standard flags:
  23. *
  24. * |q| Causes the argument to be surrounded with single quotes (|'|).
  25. * Applies to all types.
  26. *
  27. * |Q| Causes the argument to be surrounded with double quotes (|"|).
  28. * Applies to all types.
  29. *
  30. * |l| The argument is emitted in "lowercase" style.
  31. * Only applies to enums and bools.
  32. *
  33. * The following argument types are supported:
  34. *
  35. * Strings (including |const char*|, |TStringBuf|, and |TString|) and chars:
  36. * Emitted as is. Fast.
  37. *
  38. * Numerics and pointers:
  39. * Emitted using |sprintf|. Maybe not that fast.
  40. *
  41. * |bool|:
  42. * Emitted either as |True| and |False| or |true| and |false| (if lowercase mode is ON).
  43. *
  44. * Enums:
  45. * Emitted in either camel (|SomeName|) or in lowercase-with-underscores style
  46. * (|some_name|, if lowercase mode is ON).
  47. *
  48. * Nullables:
  49. * |std::nullopt| is emitted as |<null>|.
  50. *
  51. * All others:
  52. * Emitted as strings by calling |ToString|.
  53. *
  54. */
  55. template <class... TArgs>
  56. TString Format(TFormatString<TArgs...> format, TArgs&&... args);
  57. ////////////////////////////////////////////////////////////////////////////////
  58. template <class... TArgs>
  59. void Format(TStringBuilderBase* builder, TFormatString<TArgs...> format, TArgs&&... args);
  60. ////////////////////////////////////////////////////////////////////////////////
  61. template <class T>
  62. TString ToStringViaBuilder(const T& value, TStringBuf spec = TStringBuf("v"));
  63. ////////////////////////////////////////////////////////////////////////////////
  64. template <class TRange, class TFormatter>
  65. struct TFormattableView
  66. {
  67. using TBegin = std::decay_t<decltype(std::declval<const TRange>().begin())>;
  68. using TEnd = std::decay_t<decltype(std::declval<const TRange>().end())>;
  69. TBegin RangeBegin;
  70. TEnd RangeEnd;
  71. TFormatter Formatter;
  72. size_t Limit = std::numeric_limits<size_t>::max();
  73. TBegin begin() const;
  74. TEnd end() const;
  75. };
  76. //! Annotates a given #range with #formatter to be applied to each item.
  77. template <class TRange, class TFormatter>
  78. TFormattableView<TRange, TFormatter> MakeFormattableView(
  79. const TRange& range,
  80. TFormatter&& formatter);
  81. template <class TRange, class TFormatter>
  82. TFormattableView<TRange, TFormatter> MakeShrunkFormattableView(
  83. const TRange& range,
  84. TFormatter&& formatter,
  85. size_t limit);
  86. ////////////////////////////////////////////////////////////////////////////////
  87. template <class TRange, class TValueGetter, class TIntervalFormatter>
  88. struct TCompactIntervalView
  89. {
  90. using TBegin = std::decay_t<decltype(std::declval<const TRange>().begin())>;
  91. using TEnd = std::decay_t<decltype(std::declval<const TRange>().end())>;
  92. TBegin RangeBegin;
  93. TEnd RangeEnd;
  94. TValueGetter ValueGetter;
  95. TIntervalFormatter IntervalFormatter;
  96. TBegin begin() const;
  97. TEnd end() const;
  98. };
  99. template <class TRange>
  100. struct TDefaultValueGetter
  101. {
  102. using TIterator = std::decay_t<decltype(std::declval<const TRange>().begin())>;
  103. auto operator()(const TIterator& iterator) const
  104. -> typename std::iterator_traits<TIterator>::value_type;
  105. };
  106. template <class TRange, class TValueGetter>
  107. struct TDefaultIntervalFormatter
  108. {
  109. using TIterator = std::decay_t<decltype(std::declval<const TRange>().begin())>;
  110. void operator()(
  111. TStringBuilderBase* builder,
  112. const TIterator& first,
  113. const TIterator& last,
  114. const TValueGetter& valueGetter,
  115. bool firstInterval) const;
  116. };
  117. ////////////////////////////////////////////////////////////////////////////////
  118. //! Writes a given integral #range as a sequence of intervals.
  119. //! Example:
  120. // MakeCompactIntervalView(std::vector {1, 2, 3, 5, 7, 8})
  121. // => [1-3,5,7-8]
  122. template <
  123. class TRange,
  124. class TValueGetter = TDefaultValueGetter<TRange>,
  125. class TIntervalFormatter = TDefaultIntervalFormatter<TRange, TValueGetter>
  126. >
  127. TCompactIntervalView<TRange, TValueGetter, TIntervalFormatter> MakeCompactIntervalView(
  128. const TRange& range,
  129. TValueGetter&& valueGetter = {},
  130. TIntervalFormatter&& intervalFormatter = {});
  131. ////////////////////////////////////////////////////////////////////////////////
  132. template <class TFormatter>
  133. struct TFormatterWrapper
  134. {
  135. TFormatter Formatter;
  136. };
  137. // Allows insertion of text conditionally.
  138. // Usage:
  139. /*
  140. NYT::Format(
  141. "Value is %v%v",
  142. 42,
  143. MakeFormatterWrapper([&] (auto* builder) {
  144. if (PossiblyMissingInfo_) {
  145. builder->AppendString(", PossiblyMissingInfo: ");
  146. FormatValue(builder, PossiblyMissingInfo_, "v");
  147. }
  148. }));
  149. */
  150. template <class TFormatter>
  151. TFormatterWrapper<TFormatter> MakeFormatterWrapper(
  152. TFormatter&& formatter);
  153. ////////////////////////////////////////////////////////////////////////////////
  154. template <class... TArgs>
  155. class TLazyMultiValueFormatter;
  156. template <class... TArgs>
  157. void FormatValue(
  158. TStringBuilderBase* builder,
  159. const TLazyMultiValueFormatter<TArgs...>& value,
  160. TStringBuf /*spec*/);
  161. //! A wrapper for a bunch of values that formats them lazily on demand.
  162. /*!
  163. * The intended use of this class is when you need to use the same formatted string
  164. * in several places in the function (e.g. log message tags) and want both to avoid
  165. * code duplication and premature formatting of the values until necessary.
  166. *
  167. * NB: lvalues are captured by reference without lifetime extension.
  168. */
  169. template <class... TArgs>
  170. class TLazyMultiValueFormatter
  171. : private TNonCopyable
  172. {
  173. public:
  174. TLazyMultiValueFormatter(TStringBuf format, TArgs&&... args);
  175. // NB(arkady-e1ppa): We actually have to
  176. // forward declare this method as above
  177. // and friend-declare it as specialization
  178. // here because clang is stupid and would
  179. // treat this friend declartion as a hidden friend
  180. // declaration which in turn is treated as a separate symbol
  181. // causing linker to not find the actual definition.
  182. friend void FormatValue<>(
  183. TStringBuilderBase* builder,
  184. const TLazyMultiValueFormatter& value,
  185. TStringBuf /*spec*/);
  186. private:
  187. const TStringBuf Format_;
  188. const std::tuple<TArgs...> Args_;
  189. };
  190. template <class ... Args>
  191. auto MakeLazyMultiValueFormatter(TStringBuf format, Args&&... args);
  192. ////////////////////////////////////////////////////////////////////////////////
  193. /*
  194. Example:
  195. FormatVector("One: %v, Two: %v, Three: %v", {1, 2, 3})
  196. => "One: 1, Two: 2, Three: 3"
  197. */
  198. template <size_t Length, class TVector>
  199. void FormatVector(
  200. TStringBuilderBase* builder,
  201. const char (&format)[Length],
  202. const TVector& vec);
  203. template <class TVector>
  204. void FormatVector(
  205. TStringBuilderBase* builder,
  206. TStringBuf format,
  207. const TVector& vec);
  208. template <size_t Length, class TVector>
  209. TString FormatVector(
  210. const char (&format)[Length],
  211. const TVector& vec);
  212. template <class TVector>
  213. TString FormatVector(
  214. TStringBuf format,
  215. const TVector& vec);
  216. ////////////////////////////////////////////////////////////////////////////////
  217. } // namespace NYT
  218. #define FORMAT_INL_H_
  219. #include "format-inl.h"
  220. #undef FORMAT_INL_H_