format.h 7.3 KB

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