#pragma once #include "format_string.h" #include "string_builder.h" #include <util/generic/string.h> #include <iterator> namespace NYT { //////////////////////////////////////////////////////////////////////////////// /* * Format: a type-safe and fast formatting utility. * * Basically works as a type-safe analogue of |sprintf| and is expected to * be backwards-compatible with the latter. * * Like Go's |Sprintf|, supports the ultimate format specifier |v| * causing arguments to be emitted in default format. * This is the default and preferred way of formatting things, * which should be used in newer code. * * |Format| may currently invoke |sprintf| internally for emitting numeric and some other * types. You can always write your own optimized implementation, if you wish :) * * In additional to the usual |sprintf|, supports a number of non-standard flags: * * |q| Causes the argument to be surrounded with single quotes (|'|). * Applies to all types. * * |Q| Causes the argument to be surrounded with double quotes (|"|). * Applies to all types. * * |l| The argument is emitted in "lowercase" style. * Only applies to enums and bools. * * The following argument types are supported: * * Strings (including |const char*|, |TStringBuf|, and |TString|) and chars: * Emitted as is. Fast. * * Numerics and pointers: * Emitted using |sprintf|. Maybe not that fast. * * |bool|: * Emitted either as |True| and |False| or |true| and |false| (if lowercase mode is ON). * * Enums: * Emitted in either camel (|SomeName|) or in lowercase-with-underscores style * (|some_name|, if lowercase mode is ON). * * Nullables: * |std::nullopt| is emitted as |<null>|. * * All others: * Emitted as strings by calling |ToString|. * */ template <class... TArgs> TString Format(TFormatString<TArgs...> format, TArgs&&... args); //////////////////////////////////////////////////////////////////////////////// template <class... TArgs> void Format(TStringBuilderBase* builder, TFormatString<TArgs...> format, TArgs&&... args); //////////////////////////////////////////////////////////////////////////////// template <class T> TString ToStringViaBuilder(const T& value, TStringBuf spec = TStringBuf("v")); //////////////////////////////////////////////////////////////////////////////// template <class TRange, class TFormatter> struct TFormattableView { using TBegin = std::decay_t<decltype(std::declval<const TRange>().begin())>; using TEnd = std::decay_t<decltype(std::declval<const TRange>().end())>; TBegin RangeBegin; TEnd RangeEnd; TFormatter Formatter; size_t Limit = std::numeric_limits<size_t>::max(); TBegin begin() const; TEnd end() const; }; //! Annotates a given #range with #formatter to be applied to each item. template <class TRange, class TFormatter> TFormattableView<TRange, TFormatter> MakeFormattableView( const TRange& range, TFormatter&& formatter); template <class TRange, class TFormatter> TFormattableView<TRange, TFormatter> MakeShrunkFormattableView( const TRange& range, TFormatter&& formatter, size_t limit); //////////////////////////////////////////////////////////////////////////////// template <class TRange, class TValueGetter, class TIntervalFormatter> struct TCompactIntervalView { using TBegin = std::decay_t<decltype(std::declval<const TRange>().begin())>; using TEnd = std::decay_t<decltype(std::declval<const TRange>().end())>; TBegin RangeBegin; TEnd RangeEnd; TValueGetter ValueGetter; TIntervalFormatter IntervalFormatter; TBegin begin() const; TEnd end() const; }; template <class TRange> struct TDefaultValueGetter { using TIterator = std::decay_t<decltype(std::declval<const TRange>().begin())>; auto operator()(const TIterator& iterator) const -> typename std::iterator_traits<TIterator>::value_type; }; template <class TRange, class TValueGetter> struct TDefaultIntervalFormatter { using TIterator = std::decay_t<decltype(std::declval<const TRange>().begin())>; void operator()( TStringBuilderBase* builder, const TIterator& first, const TIterator& last, const TValueGetter& valueGetter, bool firstInterval) const; }; //////////////////////////////////////////////////////////////////////////////// //! Writes a given integral #range as a sequence of intervals. //! Example: // MakeCompactIntervalView(std::vector {1, 2, 3, 5, 7, 8}) // => [1-3,5,7-8] template < class TRange, class TValueGetter = TDefaultValueGetter<TRange>, class TIntervalFormatter = TDefaultIntervalFormatter<TRange, TValueGetter> > TCompactIntervalView<TRange, TValueGetter, TIntervalFormatter> MakeCompactIntervalView( const TRange& range, TValueGetter&& valueGetter = {}, TIntervalFormatter&& intervalFormatter = {}); //////////////////////////////////////////////////////////////////////////////// template <class TFormatter> struct TFormatterWrapper { TFormatter Formatter; }; // Allows insertion of text conditionally. // Usage: /* NYT::Format( "Value is %v%v", 42, MakeFormatterWrapper([&] (auto* builder) { if (PossiblyMissingInfo_) { builder->AppendString(", PossiblyMissingInfo: "); FormatValue(builder, PossiblyMissingInfo_, "v"); } })); */ template <class TFormatter> TFormatterWrapper<TFormatter> MakeFormatterWrapper( TFormatter&& formatter); //////////////////////////////////////////////////////////////////////////////// template <class... TArgs> class TLazyMultiValueFormatter; template <class... TArgs> void FormatValue( TStringBuilderBase* builder, const TLazyMultiValueFormatter<TArgs...>& value, TStringBuf /*spec*/); //! A wrapper for a bunch of values that formats them lazily on demand. /*! * The intended use of this class is when you need to use the same formatted string * in several places in the function (e.g. log message tags) and want both to avoid * code duplication and premature formatting of the values until necessary. * * NB: lvalues are captured by reference without lifetime extension. */ template <class... TArgs> class TLazyMultiValueFormatter : private TNonCopyable { public: TLazyMultiValueFormatter(TStringBuf format, TArgs&&... args); // NB(arkady-e1ppa): We actually have to // forward declare this method as above // and friend-declare it as specialization // here because clang is stupid and would // treat this friend declartion as a hidden friend // declaration which in turn is treated as a separate symbol // causing linker to not find the actual definition. friend void FormatValue<>( TStringBuilderBase* builder, const TLazyMultiValueFormatter& value, TStringBuf /*spec*/); private: const TStringBuf Format_; const std::tuple<TArgs...> Args_; }; template <class ... Args> auto MakeLazyMultiValueFormatter(TStringBuf format, Args&&... args); //////////////////////////////////////////////////////////////////////////////// /* Example: FormatVector("One: %v, Two: %v, Three: %v", {1, 2, 3}) => "One: 1, Two: 2, Three: 3" */ template <size_t Length, class TVector> void FormatVector( TStringBuilderBase* builder, const char (&format)[Length], const TVector& vec); template <class TVector> void FormatVector( TStringBuilderBase* builder, TStringBuf format, const TVector& vec); template <size_t Length, class TVector> TString FormatVector( const char (&format)[Length], const TVector& vec); template <class TVector> TString FormatVector( TStringBuf format, const TVector& vec); //////////////////////////////////////////////////////////////////////////////// } // namespace NYT #define FORMAT_INL_H_ #include "format-inl.h" #undef FORMAT_INL_H_