diff options
author | arkady-e1ppa <arkady-e1ppa@yandex-team.com> | 2024-06-07 13:13:50 +0300 |
---|---|---|
committer | arkady-e1ppa <arkady-e1ppa@yandex-team.com> | 2024-06-07 13:27:39 +0300 |
commit | 327232940ecda2082cbcd9551049456522bbaffe (patch) | |
tree | b9a4b9604260773016c98f1560f59e2d06ce535e | |
parent | 1924960f5dfa85e96d87306b3962f57862ab98c8 (diff) | |
download | ydb-327232940ecda2082cbcd9551049456522bbaffe.tar.gz |
YT-21868: Refactor NYT::Format
NYT::Format had several problems:
1. There are too many ways to enable printing of T. Not all are equally good. You could specialize TValueFormatter, you could write an overload of FormatValue, you could write an overload of ToString, you could write an overload of operator << for special stream or you could specialize the Out function.
2. If you attempt to print T which cannot be printed, you get a linker error without a proper source location which is very frustrating to work with.
3. There is no static analysis of format string performed even when it is possible.
4. If you write FormatValue overload, you still have to write ToString overload if you want to use this function (and people tend to use it quite a bit, since it is defined for util types and enums.
This pr addresses these issues to some extent. Relevant changes:
1. The only way to support NYT::Format is to define the FormatValue overload. Otherwise, you get a compile-time error.
2. Format overloads have changed: Now you have two options for general use:
```
TString Format(TStaticFormat<TArgs...> fmt, TArgs&&... args);
TString Format(TRuntimeFormat fmt, TArgs&&... args);
```
Either overload checks if TArg has a FormatValue overload. TStaticFormat performs a compile-time check of flags and the argument count. It binds to any string literal and constexpr string/string_view (and TStringBuf). TRuntimeFormat has to be mentioned explicitly. Otherwise, you will get a compile-time error for using runtime variable as a format.
3(!!!). Types which name begins with NYT:: have a specialization of ToString function which uses FormatValue. Thus, if you write class in namespace NYT and define FormatValue, you get ToString automatically. If your type is not from namespace enclosing NYT, you can just call NYT::ToString for the same effect. This limitation was caused by the fact, that we cannot review all of the external projects code which might inherit from stl classes or adopt some other questionable code practises which may completely break the dispatching mechanism of ToString due to the specialization (there were such cases). Proper documentation of this library will be added soon, so that this interaction is made known. This limitation might be lifted later
77beb68082e10aaf48be1842aad8aba63f26c1bd
66 files changed, 1301 insertions, 875 deletions
diff --git a/library/cpp/yt/logging/logger-inl.h b/library/cpp/yt/logging/logger-inl.h index b20526532d..a334a37142 100644 --- a/library/cpp/yt/logging/logger-inl.h +++ b/library/cpp/yt/logging/logger-inl.h @@ -24,7 +24,7 @@ inline bool TLogger::IsAnchorUpToDate(const TLoggingAnchor& position) const template <class... TArgs> void TLogger::AddTag(const char* format, TArgs&&... args) { - AddRawTag(Format(format, std::forward<TArgs>(args)...)); + AddRawTag(Format(TRuntimeFormat{format}, std::forward<TArgs>(args)...)); } template <class TType> diff --git a/library/cpp/yt/logging/logger.h b/library/cpp/yt/logging/logger.h index c4a4e6adef..5cbd681876 100644 --- a/library/cpp/yt/logging/logger.h +++ b/library/cpp/yt/logging/logger.h @@ -22,7 +22,7 @@ #include <atomic> -#if (__clang__ || __clang_major__ < 16) +#if (!__clang__ || __clang_major__ < 16) #define YT_DISABLE_FORMAT_STATIC_ANALYSIS #endif diff --git a/library/cpp/yt/logging/static_analysis-inl.h b/library/cpp/yt/logging/static_analysis-inl.h index d4ec5343bc..13cd425d58 100644 --- a/library/cpp/yt/logging/static_analysis-inl.h +++ b/library/cpp/yt/logging/static_analysis-inl.h @@ -6,7 +6,7 @@ #include <library/cpp/yt/misc/preprocessor.h> -#include <library/cpp/yt/string/format_analyser.h> +#include <library/cpp/yt/string/format.h> #include <string_view> #include <variant> // monostate @@ -20,6 +20,12 @@ template <class T> struct TLoggerFormatArg { }; +// Required for TLoggerFormatArg to inherit CFormattable concept +// from T. +template <class T> + requires CFormattable<T> +void FormatValue(TStringBuilderBase*, const TLoggerFormatArg<T>&, TStringBuf); + //////////////////////////////////////////////////////////////////////////////// // Stateless constexpr way of capturing arg types @@ -32,7 +38,8 @@ struct TLoggerFormatArgs // Used for macro conversion. Purposefully undefined. template <class... TArgs> -TLoggerFormatArgs<TArgs...> AsFormatArgs(TArgs&&...); +TLoggerFormatArgs<std::remove_cvref_t<TArgs>...> +AsFormatArgs(TArgs&&...); //////////////////////////////////////////////////////////////////////////////// @@ -135,5 +142,5 @@ struct NYT::TFormatArg<NYT::NLogging::NDetail::TLoggerFormatArg<T>> // "\"Value: %\" \"u\"" // Thus adding a \" \" sequence. static constexpr auto FlagSpecifiers - = TFormatArgBase::ExtendFlags</*Hot*/ false, 2, std::array{'\"', ' '}, /*TFrom*/ NYT::TFormatArg<T>>(); + = TFormatArgBase::ExtendFlags</*Hot*/ false, 2, std::array{'\"', ' '}, /*TFrom*/ T>(); }; diff --git a/library/cpp/yt/memory/ref.cpp b/library/cpp/yt/memory/ref.cpp index b5b636c9db..c01094846e 100644 --- a/library/cpp/yt/memory/ref.cpp +++ b/library/cpp/yt/memory/ref.cpp @@ -6,6 +6,8 @@ #include <library/cpp/yt/misc/port.h> +#include <library/cpp/yt/string/format.h> + #include <util/system/info.h> #include <util/system/align.h> @@ -304,24 +306,24 @@ TSharedMutableRef TSharedMutableRef::MakeCopy(TRef ref, TRefCountedTypeCookie ta //////////////////////////////////////////////////////////////////////////////// -TString ToString(TRef ref) +void FormatValue(TStringBuilderBase* builder, const TRef& ref, TStringBuf spec) { - return TString(ref.Begin(), ref.End()); + FormatValue(builder, TStringBuf{ref.Begin(), ref.End()}, spec); } -TString ToString(const TMutableRef& ref) +void FormatValue(TStringBuilderBase* builder, const TMutableRef& ref, TStringBuf spec) { - return ToString(TRef(ref)); + FormatValue(builder, TRef(ref), spec); } -TString ToString(const TSharedRef& ref) +void FormatValue(TStringBuilderBase* builder, const TSharedRef& ref, TStringBuf spec) { - return ToString(TRef(ref)); + FormatValue(builder, TRef(ref), spec); } -TString ToString(const TSharedMutableRef& ref) +void FormatValue(TStringBuilderBase* builder, const TSharedMutableRef& ref, TStringBuf spec) { - return ToString(TRef(ref)); + FormatValue(builder, TRef(ref), spec); } size_t GetPageSize() diff --git a/library/cpp/yt/memory/ref.h b/library/cpp/yt/memory/ref.h index 2177778a8f..2dfe861e75 100644 --- a/library/cpp/yt/memory/ref.h +++ b/library/cpp/yt/memory/ref.h @@ -4,6 +4,8 @@ #include "range.h" #include "shared_range.h" +#include <library/cpp/yt/string/format.h> + #include <type_traits> namespace NYT { @@ -381,10 +383,10 @@ private: //////////////////////////////////////////////////////////////////////////////// -TString ToString(TRef ref); -TString ToString(const TMutableRef& ref); -TString ToString(const TSharedRef& ref); -TString ToString(const TSharedMutableRef& ref); +void FormatValue(TStringBuilderBase* builder, const TRef& ref, TStringBuf spec); +void FormatValue(TStringBuilderBase* builder, const TMutableRef& ref, TStringBuf spec); +void FormatValue(TStringBuilderBase* builder, const TSharedRef& ref, TStringBuf spec); +void FormatValue(TStringBuilderBase* builder, const TSharedMutableRef& ref, TStringBuf); size_t GetPageSize(); size_t RoundUpToPage(size_t bytes); diff --git a/library/cpp/yt/misc/wrapper_traits.h b/library/cpp/yt/misc/wrapper_traits.h index 8a6d581419..1e000ebaff 100644 --- a/library/cpp/yt/misc/wrapper_traits.h +++ b/library/cpp/yt/misc/wrapper_traits.h @@ -1,5 +1,7 @@ #pragma once +#include <util/generic/strbuf.h> + #include <concepts> #include <utility> diff --git a/library/cpp/yt/string/format-inl.h b/library/cpp/yt/string/format-inl.h index 4d9634039c..069538d84b 100644 --- a/library/cpp/yt/string/format-inl.h +++ b/library/cpp/yt/string/format-inl.h @@ -13,7 +13,11 @@ #include <library/cpp/yt/containers/enum_indexed_array.h> +#include <library/cpp/yt/misc/concepts.h> #include <library/cpp/yt/misc/enum.h> +#include <library/cpp/yt/misc/wrapper_traits.h> + +#include <util/generic/maybe.h> #include <util/system/platform.h> @@ -21,28 +25,241 @@ #include <optional> #include <span> +#if __cplusplus >= 202302L + #include <filesystem> +#endif + namespace NYT { //////////////////////////////////////////////////////////////////////////////// -static constexpr char GenericSpecSymbol = 'v'; +// Helper functions for formatting. +namespace NDetail { + +constexpr inline char IntroductorySymbol = '%'; +constexpr inline char GenericSpecSymbol = 'v'; inline bool IsQuotationSpecSymbol(char symbol) { return symbol == 'Q' || symbol == 'q'; } +//////////////////////////////////////////////////////////////////////////////// + +template <class TValue> +void FormatValueViaSprintf( + TStringBuilderBase* builder, + TValue value, + TStringBuf spec, + TStringBuf genericSpec); + +template <class TValue> +void FormatIntValue( + TStringBuilderBase* builder, + TValue value, + TStringBuf spec, + TStringBuf genericSpec); + +void FormatPointerValue( + TStringBuilderBase* builder, + const void* value, + TStringBuf spec); + +//////////////////////////////////////////////////////////////////////////////// + +// Helper concepts for matching the correct overload. +// NB(arkady-e1ppa): We prefer to hardcode the known types +// so that someone doesn't accidentally implement the +// "SimpleRange" concept and have a non-trivial +// formatting procedure at the same time. + +template <class R> +concept CKnownRange = + requires (R r) { [] <class... Ts> (std::vector<Ts...>) { } (r); } || + requires (R r) { [] <class T, size_t E> (std::span<T, E>) { } (r); } || + requires (R r) { [] <class T, size_t N> (TCompactVector<T, N>) { } (r); } || + requires (R r) { [] <class... Ts> (std::set<Ts...>) { } (r); } || + requires (R r) { [] <class... Ts> (THashSet<Ts...>) { } (r); } || + requires (R r) { [] <class... Ts> (THashMultiSet<Ts...>) { } (r); }; + +//////////////////////////////////////////////////////////////////////////////// + +template <class R> +concept CKnownKVRange = + requires (R r) { [] <class... Ts> (std::map<Ts...>) { } (r); } || + requires (R r) { [] <class... Ts> (std::multimap<Ts...>) { } (r); } || + requires (R r) { [] <class... Ts> (THashMap<Ts...>) { } (r); } || + requires (R r) { [] <class... Ts> (THashMultiMap<Ts...>) { } (r); }; + +} // namespace NDetail + +//////////////////////////////////////////////////////////////////////////////// + +template <class TRange, class TFormatter> +void FormatRange(TStringBuilderBase* builder, const TRange& range, const TFormatter& formatter, size_t limit = std::numeric_limits<size_t>::max()) +{ + builder->AppendChar('['); + size_t index = 0; + for (const auto& item : range) { + if (index > 0) { + builder->AppendString(DefaultJoinToStringDelimiter); + } + if (index == limit) { + builder->AppendString(DefaultRangeEllipsisFormat); + break; + } + formatter(builder, item); + ++index; + } + builder->AppendChar(']'); +} + +//////////////////////////////////////////////////////////////////////////////// + +template <class TRange, class TFormatter> +void FormatKeyValueRange(TStringBuilderBase* builder, const TRange& range, const TFormatter& formatter, size_t limit = std::numeric_limits<size_t>::max()) +{ + builder->AppendChar('{'); + size_t index = 0; + for (const auto& item : range) { + if (index > 0) { + builder->AppendString(DefaultJoinToStringDelimiter); + } + if (index == limit) { + builder->AppendString(DefaultRangeEllipsisFormat); + break; + } + formatter(builder, item.first); + builder->AppendString(DefaultKeyValueDelimiter); + formatter(builder, item.second); + ++index; + } + builder->AppendChar('}'); +} + +//////////////////////////////////////////////////////////////////////////////// + +template <class R> +concept CFormattableRange = + NDetail::CKnownRange<R> && + CFormattable<typename R::value_type>; + +template <class R> +concept CFormattableKVRange = + NDetail::CKnownKVRange<R> && + CFormattable<typename R::key_type> && + CFormattable<typename R::value_type>; + +//////////////////////////////////////////////////////////////////////////////// + +template <class TRange, class TFormatter> +typename TFormattableView<TRange, TFormatter>::TBegin TFormattableView<TRange, TFormatter>::begin() const +{ + return RangeBegin; +} + +template <class TRange, class TFormatter> +typename TFormattableView<TRange, TFormatter>::TEnd TFormattableView<TRange, TFormatter>::end() const +{ + return RangeEnd; +} + +template <class TRange, class TFormatter> +TFormattableView<TRange, TFormatter> MakeFormattableView( + const TRange& range, + TFormatter&& formatter) +{ + return TFormattableView<TRange, std::decay_t<TFormatter>>{range.begin(), range.end(), std::forward<TFormatter>(formatter)}; +} + +template <class TRange, class TFormatter> +TFormattableView<TRange, TFormatter> MakeShrunkFormattableView( + const TRange& range, + TFormatter&& formatter, + size_t limit) +{ + return TFormattableView<TRange, std::decay_t<TFormatter>>{ + range.begin(), + range.end(), + std::forward<TFormatter>(formatter), + limit}; +} + +template <class TFormatter> +TFormatterWrapper<TFormatter> MakeFormatterWrapper( + TFormatter&& formatter) +{ + return TFormatterWrapper<TFormatter>{ + .Formatter = std::move(formatter) + }; +} + +template <class... TArgs> +TLazyMultiValueFormatter<TArgs...>::TLazyMultiValueFormatter( + TStringBuf format, + TArgs&&... args) + : Format_(format) + , Args_(std::forward<TArgs>(args)...) +{ } + +template <class... TArgs> +auto MakeLazyMultiValueFormatter(TStringBuf format, TArgs&&... args) +{ + return TLazyMultiValueFormatter<TArgs...>(format, std::forward<TArgs>(args)...); +} + +//////////////////////////////////////////////////////////////////////////////// + +// Non-container objects. + +#define XX(valueType, castType, genericSpec) \ + inline void FormatValue(TStringBuilderBase* builder, valueType value, TStringBuf spec) \ + { \ + NYT::NDetail::FormatIntValue(builder, static_cast<castType>(value), spec, genericSpec); \ + } + +XX(i8, i32, TStringBuf("d")) +XX(ui8, ui32, TStringBuf("u")) +XX(i16, i32, TStringBuf("d")) +XX(ui16, ui32, TStringBuf("u")) +XX(i32, i32, TStringBuf("d")) +XX(ui32, ui32, TStringBuf("u")) +XX(long, i64, TStringBuf(PRIdLEAST64)) +XX(long long, i64, TStringBuf(PRIdLEAST64)) +XX(unsigned long, ui64, TStringBuf(PRIuLEAST64)) +XX(unsigned long long, ui64, TStringBuf(PRIuLEAST64)) + +#undef XX + +#define XX(valueType, castType, genericSpec) \ + inline void FormatValue(TStringBuilderBase* builder, valueType value, TStringBuf spec) \ + { \ + NYT::NDetail::FormatValueViaSprintf(builder, static_cast<castType>(value), spec, genericSpec); \ + } + +XX(double, double, TStringBuf("lf")) +XX(float, float, TStringBuf("f")) + +#undef XX + +// Pointer +template <class T> +void FormatValue(TStringBuilderBase* builder, T* value, TStringBuf spec) +{ + NYT::NDetail::FormatPointerValue(builder, static_cast<const void*>(value), spec); +} + // TStringBuf -inline void FormatValue(TStringBuilderBase* builder, TStringBuf value, TStringBuf format) +inline void FormatValue(TStringBuilderBase* builder, TStringBuf value, TStringBuf spec) { - if (!format) { + if (!spec) { builder->AppendString(value); return; } // Parse alignment. bool alignLeft = false; - const char* current = format.begin(); + const char* current = spec.begin(); if (*current == '-') { alignLeft = true; ++current; @@ -75,7 +292,7 @@ inline void FormatValue(TStringBuilderBase* builder, TStringBuf value, TStringBu bool singleQuotes = false; bool doubleQuotes = false; bool escape = false; - while (current < format.end()) { + while (current < spec.end()) { switch (*current++) { case 'q': singleQuotes = true; @@ -123,40 +340,66 @@ inline void FormatValue(TStringBuilderBase* builder, TStringBuf value, TStringBu } // TString -inline void FormatValue(TStringBuilderBase* builder, const TString& value, TStringBuf format) +inline void FormatValue(TStringBuilderBase* builder, const TString& value, TStringBuf spec) { - FormatValue(builder, TStringBuf(value), format); + FormatValue(builder, TStringBuf(value), spec); } // const char* -inline void FormatValue(TStringBuilderBase* builder, const char* value, TStringBuf format) +inline void FormatValue(TStringBuilderBase* builder, const char* value, TStringBuf spec) +{ + FormatValue(builder, TStringBuf(value), spec); +} + +template <size_t N> +inline void FormatValue(TStringBuilderBase* builder, const char (&value)[N], TStringBuf spec) { - FormatValue(builder, TStringBuf(value), format); + FormatValue(builder, TStringBuf(value), spec); } // char* -inline void FormatValue(TStringBuilderBase* builder, char* value, TStringBuf format) +inline void FormatValue(TStringBuilderBase* builder, char* value, TStringBuf spec) { - FormatValue(builder, TStringBuf(value), format); + FormatValue(builder, TStringBuf(value), spec); } +// std::string +inline void FormatValue(TStringBuilderBase* builder, const std::string& value, TStringBuf spec) +{ + FormatValue(builder, TStringBuf(value), spec); +} + +// std::string_view +inline void FormatValue(TStringBuilderBase* builder, const std::string_view& value, TStringBuf spec) +{ + FormatValue(builder, TStringBuf(value), spec); +} + +#if __cplusplus >= 202302L +// std::filesystem::path +inline void FormatValue(TStringBuilderBase* builder, const std::filesystem::path& value, TStringBuf spec) +{ + FormatValue(builder, std::string(value), spec); +} +#endif + // char -inline void FormatValue(TStringBuilderBase* builder, char value, TStringBuf format) +inline void FormatValue(TStringBuilderBase* builder, char value, TStringBuf spec) { - FormatValue(builder, TStringBuf(&value, 1), format); + FormatValue(builder, TStringBuf(&value, 1), spec); } // bool -inline void FormatValue(TStringBuilderBase* builder, bool value, TStringBuf format) +inline void FormatValue(TStringBuilderBase* builder, bool value, TStringBuf spec) { // Parse custom flags. bool lowercase = false; - const char* current = format.begin(); - while (current != format.end()) { + const char* current = spec.begin(); + while (current != spec.end()) { if (*current == 'l') { ++current; lowercase = true; - } else if (IsQuotationSpecSymbol(*current)) { + } else if (NYT::NDetail::IsQuotationSpecSymbol(*current)) { ++current; } else break; @@ -169,399 +412,316 @@ inline void FormatValue(TStringBuilderBase* builder, bool value, TStringBuf form builder->AppendString(str); } -// Fallback to ToString -struct TToStringFallbackValueFormatterTag -{ }; +// TDuration +inline void FormatValue(TStringBuilderBase* builder, TDuration value, TStringBuf /*spec*/) +{ + builder->AppendFormat("%vus", value.MicroSeconds()); +} -template <class TValue, class = void> -struct TValueFormatter +// TInstant +inline void FormatValue(TStringBuilderBase* builder, TInstant value, TStringBuf spec) { - static TToStringFallbackValueFormatterTag Do(TStringBuilderBase* builder, const TValue& value, TStringBuf format) - { - using ::ToString; - FormatValue(builder, ToString(value), format); - return {}; - } -}; + // TODO(babenko): Optimize. + FormatValue(builder, NYT::ToStringIgnoringFormatValue(value), spec); +} // Enum template <class TEnum> -struct TValueFormatter<TEnum, typename std::enable_if<TEnumTraits<TEnum>::IsEnum>::type> + requires (TEnumTraits<TEnum>::IsEnum) +void FormatValue(TStringBuilderBase* builder, TEnum value, TStringBuf spec) { - static void Do(TStringBuilderBase* builder, TEnum value, TStringBuf format) - { - // Parse custom flags. - bool lowercase = false; - const char* current = format.begin(); - while (current != format.end()) { - if (*current == 'l') { - ++current; - lowercase = true; - } else if (IsQuotationSpecSymbol(*current)) { - ++current; - } else { - break; - } + // Parse custom flags. + bool lowercase = false; + const char* current = spec.begin(); + while (current != spec.end()) { + if (*current == 'l') { + ++current; + lowercase = true; + } else if (NYT::NDetail::IsQuotationSpecSymbol(*current)) { + ++current; + } else { + break; } - - FormatEnum(builder, value, lowercase); } -}; -template <class TRange, class TFormatter> -typename TFormattableView<TRange, TFormatter>::TBegin TFormattableView<TRange, TFormatter>::begin() const -{ - return RangeBegin; + FormatEnum(builder, value, lowercase); } -template <class TRange, class TFormatter> -typename TFormattableView<TRange, TFormatter>::TEnd TFormattableView<TRange, TFormatter>::end() const -{ - return RangeEnd; +template <class TArcadiaEnum> + requires (std::is_enum_v<TArcadiaEnum> && !TEnumTraits<TArcadiaEnum>::IsEnum) +void FormatValue(TStringBuilderBase* builder, TArcadiaEnum value, TStringBuf /*spec*/) +{ + // NB(arkady-e1ppa): This can catch normal enums which + // just want to be serialized as numbers. + // Unfortunately, we have no way of determining that other than + // marking every relevant arcadia enum in the code by trait + // or writing their complete trait and placing such trait in + // every single file where it is formatted. + // We gotta figure something out but until that + // we will just have to make a string for such enums. + // If only arcadia enums provided compile-time check + // if enum is serializable :(((((. + builder->AppendString(NYT::ToStringIgnoringFormatValue(value)); } +// Container objects. +// NB(arkady-e1ppa): In order to support container combinations +// we forward-declare them before defining. + +// TMaybe +template <class T, class TPolicy> +void FormatValue(TStringBuilderBase* builder, const TMaybe<T, TPolicy>& value, TStringBuf spec); + +// std::optional +template <class T> +void FormatValue(TStringBuilderBase* builder, const std::optional<T>& value, TStringBuf spec); + +// std::pair +template <class A, class B> +void FormatValue(TStringBuilderBase* builder, const std::pair<A, B>& value, TStringBuf spec); + +// std::tuple +template <class... Ts> +void FormatValue(TStringBuilderBase* builder, const std::tuple<Ts...>& value, TStringBuf spec); + +// TEnumIndexedArray +template <class E, class T> +void FormatValue(TStringBuilderBase* builder, const TEnumIndexedArray<E, T>& collection, TStringBuf spec); + +// One-valued ranges +template <CFormattableRange TRange> +void FormatValue(TStringBuilderBase* builder, const TRange& collection, TStringBuf spec); + +// Two-valued ranges +template <CFormattableKVRange TRange> +void FormatValue(TStringBuilderBase* builder, const TRange& collection, TStringBuf spec); + +// FormattableView template <class TRange, class TFormatter> -TFormattableView<TRange, TFormatter> MakeFormattableView( - const TRange& range, - TFormatter&& formatter) +void FormatValue( + TStringBuilderBase* builder, + const TFormattableView<TRange, TFormatter>& formattableView, + TStringBuf spec); + +// TFormatterWrapper +template <class TFormatter> +void FormatValue( + TStringBuilderBase* builder, + const TFormatterWrapper<TFormatter>& wrapper, + TStringBuf spec); + +// TLazyMultiValueFormatter +template <class... TArgs> +void FormatValue( + TStringBuilderBase* builder, + const TLazyMultiValueFormatter<TArgs...>& value, + TStringBuf /*spec*/); + +// TMaybe +template <class T, class TPolicy> +void FormatValue(TStringBuilderBase* builder, const TMaybe<T, TPolicy>& value, TStringBuf spec) { - return TFormattableView<TRange, std::decay_t<TFormatter>>{range.begin(), range.end(), std::forward<TFormatter>(formatter)}; + FormatValue(builder, NYT::ToStringIgnoringFormatValue(value), spec); } -template <class TRange, class TFormatter> -TFormattableView<TRange, TFormatter> MakeShrunkFormattableView( - const TRange& range, - TFormatter&& formatter, - size_t limit) +// std::optional: nullopt +inline void FormatValue(TStringBuilderBase* builder, std::nullopt_t, TStringBuf /*spec*/) { - return TFormattableView<TRange, std::decay_t<TFormatter>>{range.begin(), range.end(), std::forward<TFormatter>(formatter), limit}; + builder->AppendString(TStringBuf("<null>")); } -template <class TRange, class TFormatter> -void FormatRange(TStringBuilderBase* builder, const TRange& range, const TFormatter& formatter, size_t limit = std::numeric_limits<size_t>::max()) +// std::optional: generic T +template <class T> +void FormatValue(TStringBuilderBase* builder, const std::optional<T>& value, TStringBuf spec) { - builder->AppendChar('['); - size_t index = 0; - for (const auto& item : range) { - if (index > 0) { - builder->AppendString(DefaultJoinToStringDelimiter); - } - if (index == limit) { - builder->AppendString(DefaultRangeEllipsisFormat); - break; - } - formatter(builder, item); - ++index; + if (value.has_value()) { + FormatValue(builder, *value, spec); + } else { + FormatValue(builder, std::nullopt, spec); } - builder->AppendChar(']'); } -template <class TRange, class TFormatter> -void FormatKeyValueRange(TStringBuilderBase* builder, const TRange& range, const TFormatter& formatter, size_t limit = std::numeric_limits<size_t>::max()) +// std::pair +template <class A, class B> +void FormatValue(TStringBuilderBase* builder, const std::pair<A, B>& value, TStringBuf spec) { builder->AppendChar('{'); - size_t index = 0; - for (const auto& item : range) { - if (index > 0) { - builder->AppendString(DefaultJoinToStringDelimiter); - } - if (index == limit) { - builder->AppendString(DefaultRangeEllipsisFormat); - break; - } - formatter(builder, item.first); - builder->AppendString(DefaultKeyValueDelimiter); - formatter(builder, item.second); - ++index; - } + FormatValue(builder, value.first, spec); + builder->AppendString(TStringBuf(", ")); + FormatValue(builder, value.second, spec); builder->AppendChar('}'); } -// TFormattableView -template <class TRange, class TFormatter> -struct TValueFormatter<TFormattableView<TRange, TFormatter>> +// std::tuple +template <class... Ts> +void FormatValue(TStringBuilderBase* builder, const std::tuple<Ts...>& value, TStringBuf spec) { - static void Do(TStringBuilderBase* builder, const TFormattableView<TRange, TFormatter>& range, TStringBuf /*format*/) - { - FormatRange(builder, range, range.Formatter, range.Limit); - } -}; + builder->AppendChar('{'); -template <class TFormatter> -TFormatterWrapper<TFormatter> MakeFormatterWrapper( - TFormatter&& formatter) -{ - return TFormatterWrapper<TFormatter>{ - .Formatter = std::move(formatter) - }; + [&] <size_t... Idx> (std::index_sequence<Idx...>) { + ([&] { + FormatValue(builder, std::get<Idx>(value), spec); + if constexpr (Idx != sizeof...(Ts)) { + builder->AppendString(TStringBuf(", ")); + } + } (), ...); + } (std::index_sequence_for<Ts...>()); + + builder->AppendChar('}'); } -// TFormatterWrapper -template <class TFormatter> -struct TValueFormatter<TFormatterWrapper<TFormatter>> +// TEnumIndexedArray +template <class E, class T> +void FormatValue(TStringBuilderBase* builder, const TEnumIndexedArray<E, T>& collection, TStringBuf spec) { - static void Do(TStringBuilderBase* builder, const TFormatterWrapper<TFormatter>& wrapper, TStringBuf /*format*/) - { - wrapper.Formatter(builder); + builder->AppendChar('{'); + bool firstItem = true; + for (const auto& index : TEnumTraits<E>::GetDomainValues()) { + if (!firstItem) { + builder->AppendString(DefaultJoinToStringDelimiter); + } + FormatValue(builder, index, spec); + builder->AppendString(": "); + FormatValue(builder, collection[index], spec); + firstItem = false; } -}; + builder->AppendChar('}'); +} -// std::vector -template <class T, class TAllocator> -struct TValueFormatter<std::vector<T, TAllocator>> +// One-valued ranges +template <CFormattableRange TRange> +void FormatValue(TStringBuilderBase* builder, const TRange& collection, TStringBuf /*spec*/) { - static void Do(TStringBuilderBase* builder, const std::vector<T, TAllocator>& collection, TStringBuf /*format*/) - { - FormatRange(builder, collection, TDefaultFormatter()); - } -}; + NYT::FormatRange(builder, collection, TDefaultFormatter()); +} -// std::span -template <class T, size_t Extent> -struct TValueFormatter<std::span<T, Extent>> +// Two-valued ranges +template <CFormattableKVRange TRange> +void FormatValue(TStringBuilderBase* builder, const TRange& collection, TStringBuf /*spec*/) { - static void Do(TStringBuilderBase* builder, const std::span<T, Extent>& collection, TStringBuf /*format*/) - { - FormatRange(builder, collection, TDefaultFormatter()); - } -}; + NYT::FormatKeyValueRange(builder, collection, TDefaultFormatter()); +} -// TCompactVector -template <class T, unsigned N> -struct TValueFormatter<TCompactVector<T, N>> +// FormattableView +template <class TRange, class TFormatter> +void FormatValue( + TStringBuilderBase* builder, + const TFormattableView<TRange, TFormatter>& formattableView, + TStringBuf /*spec*/) { - static void Do(TStringBuilderBase* builder, const TCompactVector<T, N>& collection, TStringBuf /*format*/) - { - FormatRange(builder, collection, TDefaultFormatter()); - } -}; + NYT::FormatRange(builder, formattableView, formattableView.Formatter, formattableView.Limit); +} -// std::set -template <class T> -struct TValueFormatter<std::set<T>> +// TFormatterWrapper +template <class TFormatter> +void FormatValue( + TStringBuilderBase* builder, + const TFormatterWrapper<TFormatter>& wrapper, + TStringBuf /*spec*/) { - static void Do(TStringBuilderBase* builder, const std::set<T>& collection, TStringBuf /*format*/) - { - FormatRange(builder, collection, TDefaultFormatter()); - } -}; + wrapper.Formatter(builder); +} -// std::map -template <class K, class V> -struct TValueFormatter<std::map<K, V>> +// TLazyMultiValueFormatter +template <class... TArgs> +void FormatValue( + TStringBuilderBase* builder, + const TLazyMultiValueFormatter<TArgs...>& value, + TStringBuf /*spec*/) { - static void Do(TStringBuilderBase* builder, const std::map<K, V>& collection, TStringBuf /*format*/) - { - FormatKeyValueRange(builder, collection, TDefaultFormatter()); - } -}; + std::apply( + [&] <class... TInnerArgs> (TInnerArgs&&... args) { + builder->AppendFormat(value.Format_, std::forward<TInnerArgs>(args)...); + }, + value.Args_); +} -// std::multimap -template <class K, class V> -struct TValueFormatter<std::multimap<K, V>> -{ - static void Do(TStringBuilderBase* builder, const std::multimap<K, V>& collection, TStringBuf /*format*/) - { - FormatKeyValueRange(builder, collection, TDefaultFormatter()); - } -}; +//////////////////////////////////////////////////////////////////////////////// -// THashSet -template <class T> -struct TValueFormatter<THashSet<T>> -{ - static void Do(TStringBuilderBase* builder, const THashSet<T>& collection, TStringBuf /*format*/) - { - FormatRange(builder, collection, TDefaultFormatter()); - } -}; +namespace NDetail { -// THashMultiSet -template <class T> -struct TValueFormatter<THashMultiSet<T>> -{ - static void Do(TStringBuilderBase* builder, const THashMultiSet<T>& collection, TStringBuf /*format*/) - { - FormatRange(builder, collection, TDefaultFormatter()); - } -}; +template <size_t HeadPos, class... TArgs> +class TValueFormatter; -// THashMap -template <class K, class V> -struct TValueFormatter<THashMap<K, V>> +template <size_t HeadPos> +class TValueFormatter<HeadPos> { - static void Do(TStringBuilderBase* builder, const THashMap<K, V>& collection, TStringBuf /*format*/) +public: + void operator() (size_t /*index*/, TStringBuilderBase* builder, TStringBuf /*spec*/) const { - FormatKeyValueRange(builder, collection, TDefaultFormatter()); + builder->AppendString(TStringBuf("<missing argument>")); } }; -// THashMultiMap -template <class K, class V> -struct TValueFormatter<THashMultiMap<K, V>> +template <size_t HeadPos, class THead, class... TTail> +class TValueFormatter<HeadPos, THead, TTail...> { - static void Do(TStringBuilderBase* builder, const THashMultiMap<K, V>& collection, TStringBuf /*format*/) - { - FormatKeyValueRange(builder, collection, TDefaultFormatter()); - } -}; +public: + explicit TValueFormatter(const THead& head, const TTail&... tail) noexcept + : Head_(head) + , TailFormatter_(tail...) + { } -// TEnumIndexedArray -template <class E, class T> -struct TValueFormatter<TEnumIndexedArray<E, T>> -{ - static void Do(TStringBuilderBase* builder, const TEnumIndexedArray<E, T>& collection, TStringBuf format) + void operator() (size_t index, TStringBuilderBase* builder, TStringBuf spec) const { - builder->AppendChar('{'); - bool firstItem = true; - for (const auto& index : TEnumTraits<E>::GetDomainValues()) { - if (!firstItem) { - builder->AppendString(DefaultJoinToStringDelimiter); - } - FormatValue(builder, index, format); - builder->AppendString(": "); - FormatValue(builder, collection[index], format); - firstItem = false; + YT_ASSERT(index >= HeadPos); + if (index == HeadPos) { + FormatValue(builder, Head_, spec); + } else { + TailFormatter_(index, builder, spec); } - builder->AppendChar('}'); } -}; -// std::pair -template <class T1, class T2> -struct TValueFormatter<std::pair<T1, T2>> -{ - static void Do(TStringBuilderBase* builder, const std::pair<T1, T2>& value, TStringBuf format) - { - builder->AppendChar('{'); - FormatValue(builder, value.first, format); - builder->AppendString(TStringBuf(", ")); - FormatValue(builder, value.second, format); - builder->AppendChar('}'); - } +private: + const THead& Head_; + TValueFormatter<HeadPos + 1, TTail...> TailFormatter_; }; -// std::optional -inline void FormatValue(TStringBuilderBase* builder, std::nullopt_t, TStringBuf /*format*/) -{ - builder->AppendString(TStringBuf("<null>")); -} +//////////////////////////////////////////////////////////////////////////////// -template <class T> -struct TValueFormatter<std::optional<T>> +template <class TRangeValue> +class TRangeFormatter { - static void Do(TStringBuilderBase* builder, const std::optional<T>& value, TStringBuf format) +public: + template <class... TArgs> + requires std::constructible_from<std::span<const TRangeValue>, TArgs...> + explicit TRangeFormatter(TArgs&&... args) noexcept + : Span_(std::forward<TArgs>(args)...) + { } + + void operator() (size_t index, TStringBuilderBase* builder, TStringBuf spec) const { - if (value) { - FormatValue(builder, *value, format); + if (index >= Span_.size()) { + builder->AppendString(TStringBuf("<missing argument>")); } else { - FormatValue(builder, std::nullopt, format); + FormatValue(builder, *(Span_.begin() + index), spec); } } -}; - -template <class TValue> -auto FormatValue(TStringBuilderBase* builder, const TValue& value, TStringBuf format) -> - decltype(TValueFormatter<TValue>::Do(builder, value, format)) -{ - return TValueFormatter<TValue>::Do(builder, value, format); -} - -namespace NDetail { - -template <class TValue> -void FormatValueViaSprintf( - TStringBuilderBase* builder, - TValue value, - TStringBuf format, - TStringBuf genericSpec); - -template <class TValue> -void FormatIntValue( - TStringBuilderBase* builder, - TValue value, - TStringBuf format, - TStringBuf genericSpec); - -void FormatPointerValue( - TStringBuilderBase* builder, - const void* value, - TStringBuf format); - -} // namespace NDetail - -#define XX(valueType, castType, genericSpec) \ - inline void FormatValue(TStringBuilderBase* builder, valueType value, TStringBuf format) \ - { \ - NYT::NDetail::FormatIntValue(builder, static_cast<castType>(value), format, genericSpec); \ - } - -XX(i8, i32, TStringBuf("d")) -XX(ui8, ui32, TStringBuf("u")) -XX(i16, i32, TStringBuf("d")) -XX(ui16, ui32, TStringBuf("u")) -XX(i32, i32, TStringBuf("d")) -XX(ui32, ui32, TStringBuf("u")) -#ifdef _win_ -XX(long long, i64, TStringBuf("lld")) -XX(unsigned long long, ui64, TStringBuf("llu")) -#else -XX(long, i64, TStringBuf("ld")) -XX(unsigned long, ui64, TStringBuf("lu")) -#endif - -#undef XX - -#define XX(valueType, castType, genericSpec) \ - inline void FormatValue(TStringBuilderBase* builder, valueType value, TStringBuf format) \ - { \ - NYT::NDetail::FormatValueViaSprintf(builder, static_cast<castType>(value), format, genericSpec); \ - } -XX(double, double, TStringBuf("lf")) -XX(float, float, TStringBuf("f")) +private: + std::span<const TRangeValue> Span_; +}; -#undef XX +//////////////////////////////////////////////////////////////////////////////// -// Pointer template <class T> -void FormatValue(TStringBuilderBase* builder, T* value, TStringBuf format) -{ - NYT::NDetail::FormatPointerValue(builder, static_cast<const void*>(value), format); -} - -// TDuration (specialize for performance reasons) -inline void FormatValue(TStringBuilderBase* builder, TDuration value, TStringBuf /*format*/) -{ - builder->AppendFormat("%vus", value.MicroSeconds()); -} - -// TInstant (specialize for TFormatTraits) -inline void FormatValue(TStringBuilderBase* builder, TInstant value, TStringBuf format) -{ - // TODO(babenko): optimize - builder->AppendFormat("%v", ToString(value), format); -} +concept CFormatter = CInvocable<T, void(size_t, TStringBuilderBase*, TStringBuf)>; //////////////////////////////////////////////////////////////////////////////// -namespace NDetail { - -template <class TArgFormatter> -void FormatImpl( +template <CFormatter TFormatter> +void RunFormatter( TStringBuilderBase* builder, - TStringBuf format, - const TArgFormatter& argFormatter) + TStringBuf fmt, + const TFormatter& formatter) { size_t argIndex = 0; - auto current = format.begin(); + auto current = std::begin(fmt); + auto end = std::end(fmt); while (true) { // Scan verbatim part until stop symbol. auto verbatimBegin = current; - auto verbatimEnd = verbatimBegin; - while (verbatimEnd != format.end() && *verbatimEnd != '%') { - ++verbatimEnd; - } + auto verbatimEnd = std::find(current, end, IntroductorySymbol); // Copy verbatim part, if any. size_t verbatimSize = verbatimEnd - verbatimBegin; @@ -571,85 +731,86 @@ void FormatImpl( // Handle stop symbol. current = verbatimEnd; - if (current == format.end()) { + if (current == end) { break; } - YT_ASSERT(*current == '%'); + YT_ASSERT(*current == IntroductorySymbol); ++current; - if (*current == '%') { + if (*current == IntroductorySymbol) { // Verbatim %. - builder->AppendChar('%'); + builder->AppendChar(IntroductorySymbol); ++current; - } else { - // Scan format part until stop symbol. - auto argFormatBegin = current; - auto argFormatEnd = argFormatBegin; - bool singleQuotes = false; - bool doubleQuotes = false; - - while ( - argFormatEnd != format.end() && - *argFormatEnd != GenericSpecSymbol && // value in generic format - *argFormatEnd != 'd' && // others are standard specifiers supported by printf - *argFormatEnd != 'i' && - *argFormatEnd != 'u' && - *argFormatEnd != 'o' && - *argFormatEnd != 'x' && - *argFormatEnd != 'X' && - *argFormatEnd != 'f' && - *argFormatEnd != 'F' && - *argFormatEnd != 'e' && - *argFormatEnd != 'E' && - *argFormatEnd != 'g' && - *argFormatEnd != 'G' && - *argFormatEnd != 'a' && - *argFormatEnd != 'A' && - *argFormatEnd != 'c' && - *argFormatEnd != 's' && - *argFormatEnd != 'p' && - *argFormatEnd != 'n') - { - switch (*argFormatEnd) { - case 'q': - singleQuotes = true; - break; - case 'Q': - doubleQuotes = true; - break; - case 'h': - break; - } - ++argFormatEnd; - } + continue; + } - // Handle end of format string. - if (argFormatEnd != format.end()) { - ++argFormatEnd; + // Scan format part until stop symbol. + auto argFormatBegin = current; + auto argFormatEnd = argFormatBegin; + bool singleQuotes = false; + bool doubleQuotes = false; + + while ( + argFormatEnd != end && + *argFormatEnd != GenericSpecSymbol && // value in generic format + *argFormatEnd != 'd' && // others are standard specifiers supported by printf + *argFormatEnd != 'i' && + *argFormatEnd != 'u' && + *argFormatEnd != 'o' && + *argFormatEnd != 'x' && + *argFormatEnd != 'X' && + *argFormatEnd != 'f' && + *argFormatEnd != 'F' && + *argFormatEnd != 'e' && + *argFormatEnd != 'E' && + *argFormatEnd != 'g' && + *argFormatEnd != 'G' && + *argFormatEnd != 'a' && + *argFormatEnd != 'A' && + *argFormatEnd != 'c' && + *argFormatEnd != 's' && + *argFormatEnd != 'p' && + *argFormatEnd != 'n') + { + switch (*argFormatEnd) { + case 'q': + singleQuotes = true; + break; + case 'Q': + doubleQuotes = true; + break; + case 'h': + break; } + ++argFormatEnd; + } - // 'n' means 'nothing'; skip the argument. - if (*argFormatBegin != 'n') { - // Format argument. - TStringBuf argFormat(argFormatBegin, argFormatEnd); - if (singleQuotes) { - builder->AppendChar('\''); - } - if (doubleQuotes) { - builder->AppendChar('"'); - } - argFormatter(argIndex++, builder, argFormat); - if (singleQuotes) { - builder->AppendChar('\''); - } - if (doubleQuotes) { - builder->AppendChar('"'); - } - } + // Handle end of format string. + if (argFormatEnd != end) { + ++argFormatEnd; + } - current = argFormatEnd; + // 'n' means 'nothing'; skip the argument. + if (*argFormatBegin != 'n') { + // Format argument. + TStringBuf argFormat(argFormatBegin, argFormatEnd); + if (singleQuotes) { + builder->AppendChar('\''); + } + if (doubleQuotes) { + builder->AppendChar('"'); + } + formatter(argIndex++, builder, argFormat); + if (singleQuotes) { + builder->AppendChar('\''); + } + if (doubleQuotes) { + builder->AppendChar('"'); + } } + + current = argFormatEnd; } } @@ -658,186 +819,86 @@ void FormatImpl( //////////////////////////////////////////////////////////////////////////////// template <class... TArgs> -TLazyMultiValueFormatter<TArgs...>::TLazyMultiValueFormatter( - TStringBuf format, - TArgs&&... args) - : Format_(format) - , Args_(std::forward<TArgs>(args)...) -{ } - -template <class... TArgs> -void FormatValue( - TStringBuilderBase* builder, - const TLazyMultiValueFormatter<TArgs...>& value, - TStringBuf /*format*/) +void Format(TStringBuilderBase* builder, TStaticFormat<TArgs...> fmt, TArgs&&... args) { - std::apply( - [&] <class... TInnerArgs> (TInnerArgs&&... args) { - builder->AppendFormat(value.Format_, std::forward<TInnerArgs>(args)...); - }, - value.Args_); + NYT::NDetail::TValueFormatter<0, TArgs...> formatter(args...); + NYT::NDetail::RunFormatter(builder, fmt.Get(), formatter); } template <class... TArgs> -auto MakeLazyMultiValueFormatter(TStringBuf format, TArgs&&... args) -{ - return TLazyMultiValueFormatter<TArgs...>(format, std::forward<TArgs>(args)...); -} - -//////////////////////////////////////////////////////////////////////////////// - -template <class T> -struct TFormatTraits -{ - static constexpr bool HasCustomFormatValue = !std::is_same_v< - decltype(FormatValue( - static_cast<TStringBuilderBase*>(nullptr), - *static_cast<const T*>(nullptr), - TStringBuf())), - TToStringFallbackValueFormatterTag>; -}; - -//////////////////////////////////////////////////////////////////////////////// - -template <size_t IndexBase, class... TArgs> -struct TArgFormatterImpl; - -template <size_t IndexBase> -struct TArgFormatterImpl<IndexBase> -{ - void operator() (size_t /*index*/, TStringBuilderBase* builder, TStringBuf /*format*/) const - { - builder->AppendString(TStringBuf("<missing argument>")); - } -}; - -template <size_t IndexBase, class THeadArg, class... TTailArgs> -struct TArgFormatterImpl<IndexBase, THeadArg, TTailArgs...> -{ - explicit TArgFormatterImpl(const THeadArg& headArg, const TTailArgs&... tailArgs) - : HeadArg(headArg) - , TailFormatter(tailArgs...) - { } - - const THeadArg& HeadArg; - TArgFormatterImpl<IndexBase + 1, TTailArgs...> TailFormatter; - - void operator() (size_t index, TStringBuilderBase* builder, TStringBuf format) const - { - YT_ASSERT(index >= IndexBase); - if (index == IndexBase) { - FormatValue(builder, HeadArg, format); - } else { - TailFormatter(index, builder, format); - } - } -}; - -template <typename TVectorElement> -struct TSpanArgFormatterImpl -{ - explicit TSpanArgFormatterImpl(std::span<TVectorElement> v) - : Span_(v) - { } - - explicit TSpanArgFormatterImpl(const std::vector<TVectorElement>& v) - : Span_(v.begin(), v.size()) - { } - - explicit TSpanArgFormatterImpl(const TVector<TVectorElement>& v) - : Span_(v.begin(), v.size()) - { } - - std::span<const TVectorElement> Span_; - - void operator() (size_t index, TStringBuilderBase* builder, TStringBuf format) const - { - if (index >= Span_.size()) { - builder->AppendString(TStringBuf("<missing argument>")); - } else { - FormatValue(builder, *(Span_.begin() + index), format); - } - } -}; - -//////////////////////////////////////////////////////////////////////////////// - -template <size_t Length, class... TArgs> -void Format( - TStringBuilderBase* builder, - const char (&format)[Length], - TArgs&&... args) -{ - Format(builder, TStringBuf(format, Length - 1), std::forward<TArgs>(args)...); +void Format(TStringBuilderBase* builder, TRuntimeFormat fmt, TArgs&&... args) +{ + // NB(arkady-e1ppa): StaticFormat performs the + // formattability check of the args in a way + // that provides more useful information + // than a simple static_assert with conjunction. + // Additionally, the latter doesn't work properly + // for older clang version. + static constexpr auto argsChecker = [] { + TStaticFormat<TArgs...>::CheckFormattability(); + return 42; + } (); + Y_UNUSED(argsChecker); + + NYT::NDetail::TValueFormatter<0, TArgs...> formatter(args...); + NYT::NDetail::RunFormatter(builder, fmt.Get(), formatter); } template <class... TArgs> -void Format( - TStringBuilderBase* builder, - TStringBuf format, - TArgs&&... args) -{ - TArgFormatterImpl<0, TArgs...> argFormatter(args...); - NYT::NDetail::FormatImpl(builder, format, argFormatter); -} - -template <size_t Length, class... TArgs> -TString Format( - const char (&format)[Length], - TArgs&&... args) +TString Format(TStaticFormat<TArgs...> fmt, TArgs&&... args) { TStringBuilder builder; - Format(&builder, format, std::forward<TArgs>(args)...); + Format(&builder, fmt, std::forward<TArgs>(args)...); return builder.Flush(); } template <class... TArgs> -TString Format( - TStringBuf format, - TArgs&&... args) +TString Format(TRuntimeFormat fmt, TArgs&&... args) { TStringBuilder builder; - Format(&builder, format, std::forward<TArgs>(args)...); + Format(&builder, fmt, std::forward<TArgs>(args)...); return builder.Flush(); } +//////////////////////////////////////////////////////////////////////////////// + template <size_t Length, class TVector> void FormatVector( TStringBuilderBase* builder, - const char (&format)[Length], + const char (&fmt)[Length], const TVector& vec) { - TSpanArgFormatterImpl formatter(vec); - NYT::NDetail::FormatImpl(builder, format, formatter); + NYT::NDetail::TRangeFormatter<typename TVector::value_type> formatter(vec); + NYT::NDetail::RunFormatter(builder, fmt, formatter); } template <class TVector> void FormatVector( TStringBuilderBase* builder, - TStringBuf format, + TStringBuf fmt, const TVector& vec) { - TSpanArgFormatterImpl formatter(vec); - NYT::NDetail::FormatImpl(builder, format, formatter); + NYT::NDetail::TRangeFormatter<typename TVector::value_type> formatter(vec); + NYT::NDetail::RunFormatter(builder, fmt, formatter); } template <size_t Length, class TVector> TString FormatVector( - const char (&format)[Length], + const char (&fmt)[Length], const TVector& vec) { TStringBuilder builder; - FormatVector(&builder, format, vec); + FormatVector(&builder, fmt, vec); return builder.Flush(); } template <class TVector> TString FormatVector( - TStringBuf format, + TStringBuf fmt, const TVector& vec) { TStringBuilder builder; - FormatVector(&builder, format, vec); + FormatVector(&builder, fmt, vec); return builder.Flush(); } diff --git a/library/cpp/yt/string/format.h b/library/cpp/yt/string/format.h index 8e0d5f82e5..6463135f89 100644 --- a/library/cpp/yt/string/format.h +++ b/library/cpp/yt/string/format.h @@ -54,15 +54,15 @@ namespace NYT { * */ -template <size_t Length, class... TArgs> -void Format(TStringBuilderBase* builder, const char (&format)[Length], TArgs&&... args); template <class... TArgs> -void Format(TStringBuilderBase* builder, TStringBuf format, TArgs&&... args); +void Format(TStringBuilderBase* builder, TStaticFormat<TArgs...> fmt, TArgs&&... args); +template <class... TArgs> +void Format(TStringBuilderBase* builder, TRuntimeFormat fmt, TArgs&&... args); -template <size_t Length, class... TArgs> -TString Format(const char (&format)[Length], TArgs&&... args); template <class... TArgs> -TString Format(TStringBuf format, TArgs&&... args); +TString Format(TStaticFormat<TArgs...> fmt, TArgs&&... args); +template <class... TArgs> +TString Format(TRuntimeFormat fmt, TArgs&&... args); //////////////////////////////////////////////////////////////////////////////// @@ -101,6 +101,19 @@ 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); @@ -114,7 +127,7 @@ template <class... TArgs> void FormatValue( TStringBuilderBase* builder, const TLazyMultiValueFormatter<TArgs...>& value, - TStringBuf /*format*/); + TStringBuf /*spec*/); //! A wrapper for a bunch of values that formats them lazily on demand. /*! @@ -129,12 +142,19 @@ class TLazyMultiValueFormatter : private TNonCopyable { public: - TLazyMultiValueFormatter(TStringBuf format, TArgs&&... args); - + TLazyMultiValueFormatter(TStringBuf fmt, 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 /*format*/); + TStringBuf /*spec*/); private: const TStringBuf Format_; @@ -142,7 +162,37 @@ private: }; template <class ... Args> -auto MakeLazyMultiValueFormatter(TStringBuf format, Args&&... args); +auto MakeLazyMultiValueFormatter(TStringBuf fmt, 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 (&fmt)[Length], + const TVector& vec); + +template <class TVector> +void FormatVector( + TStringBuilderBase* builder, + TStringBuf fmt, + const TVector& vec); + +template <size_t Length, class TVector> +TString FormatVector( + const char (&fmt)[Length], + const TVector& vec); + +template <class TVector> +TString FormatVector( + TStringBuf fmt, + const TVector& vec); //////////////////////////////////////////////////////////////////////////////// diff --git a/library/cpp/yt/string/format_analyser-inl.h b/library/cpp/yt/string/format_analyser-inl.h index a548a06072..ecc83ebccb 100644 --- a/library/cpp/yt/string/format_analyser-inl.h +++ b/library/cpp/yt/string/format_analyser-inl.h @@ -4,11 +4,11 @@ #include "format_analyser.h" #endif -namespace NYT { +#include "format_arg.h" -//////////////////////////////////////////////////////////////////////////////// +namespace NYT::NDetail { -namespace NDetail { +//////////////////////////////////////////////////////////////////////////////// consteval bool Contains(std::string_view sv, char symbol) { @@ -93,7 +93,9 @@ consteval void TFormatAnalyser::ValidateFormat(std::string_view fmt) template <class TArg> consteval auto TFormatAnalyser::GetSpecifiers() { - static_assert(CFormattable<TArg>, "Your specialization of TFormatArg is broken"); + if constexpr (!CFormattable<TArg>) { + CrashCompilerNotFormattable<TArg>("Your specialization of TFormatArg is broken"); + } return TSpecifiers{ .Conversion = std::string_view{ @@ -105,39 +107,6 @@ consteval auto TFormatAnalyser::GetSpecifiers() }; } -} // namespace NDetail - //////////////////////////////////////////////////////////////////////////////// -template <bool Hot, size_t N, std::array<char, N> List, class TFrom> -consteval auto TFormatArgBase::ExtendConversion() -{ - static_assert(CFormattable<TFrom>); - return AppendArrays<Hot, N, List, &TFrom::ConversionSpecifiers>(); -} - -template <bool Hot, size_t N, std::array<char, N> List, class TFrom> -consteval auto TFormatArgBase::ExtendFlags() -{ - static_assert(CFormattable<TFrom>); - return AppendArrays<Hot, N, List, &TFrom::FlagSpecifiers>(); -} - -template <bool Hot, size_t N, std::array<char, N> List, auto* From> -consteval auto TFormatArgBase::AppendArrays() -{ - auto& from = *From; - return [] <size_t... I, size_t... J> ( - std::index_sequence<I...>, - std::index_sequence<J...>) { - if constexpr (Hot) { - return std::array{List[J]..., from[I]...}; - } else { - return std::array{from[I]..., List[J]...}; - } - } ( - std::make_index_sequence<std::size(from)>(), - std::make_index_sequence<N>()); - } - } // namespace NYT::NDetail diff --git a/library/cpp/yt/string/format_analyser.h b/library/cpp/yt/string/format_analyser.h index 4afe3f1ca8..9a05e3eb8c 100644 --- a/library/cpp/yt/string/format_analyser.h +++ b/library/cpp/yt/string/format_analyser.h @@ -1,14 +1,14 @@ #pragma once +#include <util/generic/strbuf.h> + #include <array> #include <string_view> -namespace NYT { +namespace NYT::NDetail { //////////////////////////////////////////////////////////////////////////////// -namespace NDetail { - struct TFormatAnalyser { public: @@ -18,6 +18,9 @@ public: private: // Non-constexpr function call will terminate compilation. // Purposefully undefined and non-constexpr/consteval + template <class T> + static void CrashCompilerNotFormattable(std::string_view /*msg*/) + { /*Suppress "internal linkage but undefined" warning*/ } static void CrashCompilerNotEnoughArguments(std::string_view msg); static void CrashCompilerTooManyArguments(std::string_view msg); static void CrashCompilerWrongTermination(std::string_view msg); @@ -35,67 +38,9 @@ private: static constexpr char IntroductorySymbol = '%'; }; -} // namespace NDetail - //////////////////////////////////////////////////////////////////////////////// -// Base used for flag checks for each type independently. -// Use it for overrides. -struct TFormatArgBase -{ -public: - // TODO(arkady-e1ppa): Consider more strict formatting rules. - static constexpr std::array ConversionSpecifiers = { - 'v', '1', 'c', 's', 'd', 'i', 'o', - 'x', 'X', 'u', 'f', 'F', 'e', 'E', - 'a', 'A', 'g', 'G', 'n', 'p' - }; - - static constexpr std::array FlagSpecifiers = { - '-', '+', ' ', '#', '0', - '1', '2', '3', '4', '5', - '6', '7', '8', '9', - '*', '.', 'h', 'l', 'j', - 'z', 't', 'L', 'q', 'Q' - }; - - template <class T> - static constexpr bool IsSpecifierList = requires (T t) { - [] <size_t N> (std::array<char, N>) { } (t); - }; - - // Hot = |true| adds specifiers to the beggining - // of a new array. - template <bool Hot, size_t N, std::array<char, N> List, class TFrom = TFormatArgBase> - static consteval auto ExtendConversion(); - - template <bool Hot, size_t N, std::array<char, N> List, class TFrom = TFormatArgBase> - static consteval auto ExtendFlags(); - -private: - template <bool Hot, size_t N, std::array<char, N> List, auto* From> - static consteval auto AppendArrays(); -}; - -//////////////////////////////////////////////////////////////////////////////// - -template <class T> -struct TFormatArg - : public TFormatArgBase -{ }; - -// Semantic requirement: -// Said field must be constexpr. -template <class T> -concept CFormattable = requires { - TFormatArg<T>::ConversionSpecifiers; - requires TFormatArgBase::IsSpecifierList<decltype(TFormatArg<T>::ConversionSpecifiers)>; - - TFormatArg<T>::FlagSpecifiers; - requires TFormatArgBase::IsSpecifierList<decltype(TFormatArg<T>::FlagSpecifiers)>; -}; - -} // namespace NYT +} // namespace NYT::NDetail #define FORMAT_ANALYSER_INL_H_ #include "format_analyser-inl.h" diff --git a/library/cpp/yt/string/format_arg-inl.h b/library/cpp/yt/string/format_arg-inl.h new file mode 100644 index 0000000000..82a77a1249 --- /dev/null +++ b/library/cpp/yt/string/format_arg-inl.h @@ -0,0 +1,67 @@ +#ifndef FORMAT_ARG_INL_H_ +#error "Direct inclusion of this file is not allowed, include format_arg.h" +// For the sake of sane code completion. +#include "format_arg.h" +#endif + +namespace NYT { + +//////////////////////////////////////////////////////////////////////////////// + +namespace NDetail { + +template <class T> +constexpr std::string_view QualidName() +{ + constexpr size_t openBracketOffset = 5; + constexpr size_t closeBracketOffset = 1; + constexpr std::string_view func = __PRETTY_FUNCTION__; + constexpr auto left = std::find(std::begin(func), std::end(func), '[') + openBracketOffset; + return std::string_view(left, std::prev(std::end(func), closeBracketOffset)); +} + +template <class T> +constexpr bool IsNYTName() +{ + constexpr auto qualidName = QualidName<T>(); + return qualidName.find("NYT::") == 0; +} + +} // namespace NDetail + +//////////////////////////////////////////////////////////////////////////////// + +template <bool Hot, size_t N, std::array<char, N> List, class TFrom> +consteval auto TFormatArgBase::ExtendConversion() +{ + static_assert(std::same_as<TFrom, TFormatArgBase> || CFormattable<TFrom>); + return AppendArrays<Hot, N, List, &TFormatArg<TFrom>::ConversionSpecifiers>(); +} + +template <bool Hot, size_t N, std::array<char, N> List, class TFrom> +consteval auto TFormatArgBase::ExtendFlags() +{ + static_assert(std::same_as<TFrom, TFormatArgBase> || CFormattable<TFrom>); + return AppendArrays<Hot, N, List, &TFormatArg<TFrom>::FlagSpecifiers>(); +} + +template <bool Hot, size_t N, std::array<char, N> List, auto* From> +consteval auto TFormatArgBase::AppendArrays() +{ + auto& from = *From; + return [] <size_t... I, size_t... J> ( + std::index_sequence<I...>, + std::index_sequence<J...>) { + if constexpr (Hot) { + return std::array{List[J]..., from[I]...}; + } else { + return std::array{from[I]..., List[J]...}; + } + } ( + std::make_index_sequence<std::size(from)>(), + std::make_index_sequence<N>()); +} + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT diff --git a/library/cpp/yt/string/format_arg.h b/library/cpp/yt/string/format_arg.h new file mode 100644 index 0000000000..8c4f354f98 --- /dev/null +++ b/library/cpp/yt/string/format_arg.h @@ -0,0 +1,103 @@ +#pragma once + +#include <util/generic/strbuf.h> + +#include <array> +#include <string_view> + +namespace NYT { + +//////////////////////////////////////////////////////////////////////////////// + +class TStringBuilderBase; + +//////////////////////////////////////////////////////////////////////////////// + +namespace NDetail { + +template <class T> +constexpr std::string_view QualidName(); + +template <class T> +constexpr bool IsNYTName(); + +template <class T> +concept CYtName = IsNYTName<T>(); + +} // namespace NDetail + +//////////////////////////////////////////////////////////////////////////////// + +// Base used for flag checks for each type independently. +// Use it for overrides. +struct TFormatArgBase +{ +public: + // TODO(arkady-e1ppa): Consider more strict formatting rules. + static constexpr std::array ConversionSpecifiers = { + 'v', '1', 'c', 's', 'd', 'i', 'o', + 'x', 'X', 'u', 'f', 'F', 'e', 'E', + 'a', 'A', 'g', 'G', 'n', 'p' + }; + + static constexpr std::array FlagSpecifiers = { + '-', '+', ' ', '#', '0', + '1', '2', '3', '4', '5', + '6', '7', '8', '9', + '*', '.', 'h', 'l', 'j', + 'z', 't', 'L', 'q', 'Q' + }; + + template <class T> + static constexpr bool IsSpecifierList = requires (T t) { + [] <size_t N> (std::array<char, N>) { } (t); + }; + + // Hot = |true| adds specifiers to the beggining + // of a new array. + template <bool Hot, size_t N, std::array<char, N> List, class TFrom = TFormatArgBase> + static consteval auto ExtendConversion(); + + template <bool Hot, size_t N, std::array<char, N> List, class TFrom = TFormatArgBase> + static consteval auto ExtendFlags(); + +private: + template <bool Hot, size_t N, std::array<char, N> List, auto* From> + static consteval auto AppendArrays(); +}; + +//////////////////////////////////////////////////////////////////////////////// + +template <class T> +struct TFormatArg + : public TFormatArgBase +{ }; + +// Ultimate customization point mechanism --- define an overload +// of FormatValue in order to support formatting of your type. +// Semantic requirement: +// Said field must be constexpr. +template <class T> +concept CFormattable = + requires { + TFormatArg<T>::ConversionSpecifiers; + requires TFormatArgBase::IsSpecifierList<decltype(TFormatArg<T>::ConversionSpecifiers)>; + + TFormatArg<T>::FlagSpecifiers; + requires TFormatArgBase::IsSpecifierList<decltype(TFormatArg<T>::FlagSpecifiers)>; + } && + requires ( + TStringBuilderBase* builder, + const T& value, + TStringBuf spec + ) { + { FormatValue(builder, value, spec) } -> std::same_as<void>; + }; + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT + +#define FORMAT_ARG_INL_H_ +#include "format_arg-inl.h" +#undef FORMAT_ARG_INL_H_ diff --git a/library/cpp/yt/string/format_string-inl.h b/library/cpp/yt/string/format_string-inl.h new file mode 100644 index 0000000000..5187a2ac6b --- /dev/null +++ b/library/cpp/yt/string/format_string-inl.h @@ -0,0 +1,56 @@ +#ifndef FORMAT_STRING_INL_H_ +#error "Direct inclusion of this file is not allowed, include format_string.h" +// For the sake of sane code completion. +#include "format_string.h" +#endif + +namespace NYT { + +//////////////////////////////////////////////////////////////////////////////// + +template <class... TArgs> +template <class T> + requires std::constructible_from<std::string_view, T> +consteval TBasicStaticFormat<TArgs...>::TBasicStaticFormat(const T& fmt) + : Format_(fmt) +{ + CheckFormattability(); +#if !defined(NDEBUG) && !defined(YT_DISABLE_FORMAT_STATIC_ANALYSIS) + NDetail::TFormatAnalyser::ValidateFormat<std::remove_cvref_t<TArgs>...>(Format_); +#endif +} + +template <class... TArgs> +TStringBuf TBasicStaticFormat<TArgs...>::Get() const noexcept +{ + return {Format_}; +} + +template <class... TArgs> +consteval void TBasicStaticFormat<TArgs...>::CheckFormattability() +{ +#if !defined(NDEBUG) && !defined(YT_DISABLE_FORMAT_STATIC_ANALYSIS) + using TTuple = std::tuple<std::remove_cvref_t<TArgs>...>; + + [] <size_t... Idx> (std::index_sequence<Idx...>) { + ([] { + if constexpr (!CFormattable<std::tuple_element_t<Idx, TTuple>>) { + CrashCompilerClassIsNotFormattable<std::tuple_element_t<Idx, TTuple>>(); + } + } (), ...); + } (std::index_sequence_for<TArgs...>()); +#endif +} + +inline TRuntimeFormat::TRuntimeFormat(TStringBuf fmt) + : Format_(fmt) +{ } + +inline TStringBuf TRuntimeFormat::Get() const noexcept +{ + return Format_; +} + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT diff --git a/library/cpp/yt/string/format_string.h b/library/cpp/yt/string/format_string.h new file mode 100644 index 0000000000..8eeab7d99d --- /dev/null +++ b/library/cpp/yt/string/format_string.h @@ -0,0 +1,61 @@ +#pragma once + +#include "format_analyser.h" + +#include <util/generic/strbuf.h> + +#if (!__clang__ || __clang_major__ < 16) + #define YT_DISABLE_FORMAT_STATIC_ANALYSIS +#endif + +namespace NYT { + +//////////////////////////////////////////////////////////////////////////////// + +// This class used to properly bind to +// string literals and allow compile-time parsing/checking +// of those. If you need a runtime format, use TRuntimeFormat +template <class... TArgs> +class TBasicStaticFormat +{ +public: + // Can be used to perform compile-time check of format. + template <class T> + requires std::constructible_from<std::string_view, T> + consteval TBasicStaticFormat(const T& fmt); + + TStringBuf Get() const noexcept; + + static consteval void CheckFormattability(); + +private: + std::string_view Format_; + + template <class T> + static void CrashCompilerClassIsNotFormattable(); +}; + +// Explicitly create TRuntimeFormat if you wish to +// use runtime/non-literal value as format. +class TRuntimeFormat +{ +public: + inline explicit TRuntimeFormat(TStringBuf fmt); + + inline TStringBuf Get() const noexcept; + +private: + TStringBuf Format_; +}; + +// Used to properly infer template arguments if Format. +template <class... TArgs> +using TStaticFormat = TBasicStaticFormat<std::type_identity_t<TArgs>...>; + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT + +#define FORMAT_STRING_INL_H_ +#include "format_string-inl.h" +#undef FORMAT_STRING_INL_H_ diff --git a/library/cpp/yt/string/guid.cpp b/library/cpp/yt/string/guid.cpp index 6c133a9778..2a781d032d 100644 --- a/library/cpp/yt/string/guid.cpp +++ b/library/cpp/yt/string/guid.cpp @@ -11,11 +11,6 @@ void FormatValue(TStringBuilderBase* builder, TGuid value, TStringBuf /*format*/ builder->Advance(end - begin); } -TString ToString(TGuid guid) -{ - return ToStringViaBuilder(guid); -} - //////////////////////////////////////////////////////////////////////////////// } // namespace NYT diff --git a/library/cpp/yt/string/guid.h b/library/cpp/yt/string/guid.h index 75edbce5db..de16993486 100644 --- a/library/cpp/yt/string/guid.h +++ b/library/cpp/yt/string/guid.h @@ -7,7 +7,6 @@ namespace NYT { //////////////////////////////////////////////////////////////////////////////// void FormatValue(TStringBuilderBase* builder, TGuid value, TStringBuf /*format*/); -TString ToString(TGuid guid); //////////////////////////////////////////////////////////////////////////////// diff --git a/library/cpp/yt/string/string_builder-inl.h b/library/cpp/yt/string/string_builder-inl.h index 414667954f..683a241c9c 100644 --- a/library/cpp/yt/string/string_builder-inl.h +++ b/library/cpp/yt/string/string_builder-inl.h @@ -6,6 +6,8 @@ #include <library/cpp/yt/assert/assert.h> +#include <util/stream/str.h> + namespace NYT { //////////////////////////////////////////////////////////////////////////////// @@ -81,13 +83,13 @@ inline void TStringBuilderBase::Reset() template <class... TArgs> void TStringBuilderBase::AppendFormat(TStringBuf format, TArgs&& ... args) { - Format(this, format, std::forward<TArgs>(args)...); + Format(this, TRuntimeFormat{format}, std::forward<TArgs>(args)...); } template <size_t Length, class... TArgs> void TStringBuilderBase::AppendFormat(const char (&format)[Length], TArgs&& ... args) { - Format(this, format, std::forward<TArgs>(args)...); + Format(this, TRuntimeFormat{format}, std::forward<TArgs>(args)...); } //////////////////////////////////////////////////////////////////////////////// @@ -131,4 +133,52 @@ TString ToStringViaBuilder(const T& value, TStringBuf spec) //////////////////////////////////////////////////////////////////////////////// +// Compatibility for users of NYT::ToString(nyt_type). +template <CFormattable T> +TString ToString(const T& t) +{ + return ToStringViaBuilder(t); +} + +// Sometime we want to implement +// FormatValue using util's ToString +// However, if we inside the FormatValue +// we cannot just call the ToString since +// in this scope T is already CFormattable +// and ToString will call the very +// FormatValue we are implementing, +// causing an infinite recursion loop. +// This method is basically a call to +// util's ToString default implementation. +template <class T> +TString ToStringIgnoringFormatValue(const T& t) +{ + TString s; + ::TStringOutput o(s); + o << t; + return s; +} + +//////////////////////////////////////////////////////////////////////////////// + } // namespace NYT + +#include <util/string/cast.h> + +// util/string/cast.h extension for yt types only +// TODO(arkady-e1ppa): Abolish ::ToString in +// favour of either NYT::ToString or +// automatic formatting wherever it is needed. +namespace NPrivate { + +template <class T> + requires (NYT::NDetail::CYtName<T> && NYT::CFormattable<T>) +struct TToString<T, false> +{ + static TString Cvt(const T& t) + { + return NYT::ToStringViaBuilder(t); + } +}; + +} // namespace NPrivate diff --git a/library/cpp/yt/string/string_builder.h b/library/cpp/yt/string/string_builder.h index 4ac9020177..122e5d19a0 100644 --- a/library/cpp/yt/string/string_builder.h +++ b/library/cpp/yt/string/string_builder.h @@ -1,5 +1,7 @@ #pragma once +#include "format_string.h" + #include <util/generic/string.h> namespace NYT { @@ -11,10 +13,10 @@ class TStringBuilderBase; class TStringBuilder; class TDelimitedStringBuilderWrapper; -template <size_t Length, class... TArgs> -void Format(TStringBuilderBase* builder, const char (&format)[Length], TArgs&&... args); template <class... TArgs> -void Format(TStringBuilderBase* builder, TStringBuf format, TArgs&&... args); +void Format(TStringBuilderBase* builder, TStaticFormat<TArgs...> fmt, TArgs&&... args); +template <class... TArgs> +void Format(TStringBuilderBase* builder, TRuntimeFormat fmt, TArgs&&... args); //////////////////////////////////////////////////////////////////////////////// diff --git a/library/cpp/yt/string/unittests/enum_ut.cpp b/library/cpp/yt/string/unittests/enum_ut.cpp index 2c368e85d4..be5387151a 100644 --- a/library/cpp/yt/string/unittests/enum_ut.cpp +++ b/library/cpp/yt/string/unittests/enum_ut.cpp @@ -12,8 +12,8 @@ namespace { // Some compile-time sanity checks. DEFINE_ENUM(ESample, (One)(Two)); -static_assert(TFormatTraits<ESample>::HasCustomFormatValue); -static_assert(TFormatTraits<TEnumIndexedArray<ESample, int>>::HasCustomFormatValue); +static_assert(CFormattable<ESample>); +static_assert(CFormattable<TEnumIndexedArray<ESample, int>>); DEFINE_ENUM(EColor, (Red) diff --git a/library/cpp/yt/string/unittests/format_ut.cpp b/library/cpp/yt/string/unittests/format_ut.cpp index 80162747cc..14b783516c 100644 --- a/library/cpp/yt/string/unittests/format_ut.cpp +++ b/library/cpp/yt/string/unittests/format_ut.cpp @@ -4,6 +4,8 @@ #include <library/cpp/yt/small_containers/compact_vector.h> +#include <util/generic/hash_set.h> + #include <limits> namespace NYT { @@ -12,31 +14,31 @@ namespace { //////////////////////////////////////////////////////////////////////////////// // Some compile-time sanity checks. -static_assert(TFormatTraits<int>::HasCustomFormatValue); -static_assert(TFormatTraits<double>::HasCustomFormatValue); -static_assert(TFormatTraits<void*>::HasCustomFormatValue); -static_assert(TFormatTraits<const char*>::HasCustomFormatValue); -static_assert(TFormatTraits<TStringBuf>::HasCustomFormatValue); -static_assert(TFormatTraits<TString>::HasCustomFormatValue); -static_assert(TFormatTraits<std::span<int>>::HasCustomFormatValue); -static_assert(TFormatTraits<std::vector<int>>::HasCustomFormatValue); +static_assert(CFormattable<int>); +static_assert(CFormattable<double>); +static_assert(CFormattable<void*>); +static_assert(CFormattable<const char*>); +static_assert(CFormattable<TStringBuf>); +static_assert(CFormattable<TString>); +static_assert(CFormattable<std::span<int>>); +static_assert(CFormattable<std::vector<int>>); // N.B. TCompactVector<int, 1> is not buildable on Windows -static_assert(TFormatTraits<TCompactVector<int, 2>>::HasCustomFormatValue); -static_assert(TFormatTraits<std::set<int>>::HasCustomFormatValue); -static_assert(TFormatTraits<std::map<int, int>>::HasCustomFormatValue); -static_assert(TFormatTraits<std::multimap<int, int>>::HasCustomFormatValue); -static_assert(TFormatTraits<THashSet<int>>::HasCustomFormatValue); -static_assert(TFormatTraits<THashMap<int, int>>::HasCustomFormatValue); -static_assert(TFormatTraits<THashMultiSet<int>>::HasCustomFormatValue); -static_assert(TFormatTraits<std::pair<int, int>>::HasCustomFormatValue); -static_assert(TFormatTraits<std::optional<int>>::HasCustomFormatValue); -static_assert(TFormatTraits<TDuration>::HasCustomFormatValue); -static_assert(TFormatTraits<TInstant>::HasCustomFormatValue); +static_assert(CFormattable<TCompactVector<int, 2>>); +static_assert(CFormattable<std::set<int>>); +static_assert(CFormattable<std::map<int, int>>); +static_assert(CFormattable<std::multimap<int, int>>); +static_assert(CFormattable<THashSet<int>>); +static_assert(CFormattable<THashMap<int, int>>); +static_assert(CFormattable<THashMultiSet<int>>); +static_assert(CFormattable<std::pair<int, int>>); +static_assert(CFormattable<std::optional<int>>); +static_assert(CFormattable<TDuration>); +static_assert(CFormattable<TInstant>); struct TUnformattable { }; -static_assert(!TFormatTraits<TUnformattable>::HasCustomFormatValue); +static_assert(!CFormattable<TUnformattable>); //////////////////////////////////////////////////////////////////////////////// diff --git a/library/cpp/yt/string/unittests/guid_ut.cpp b/library/cpp/yt/string/unittests/guid_ut.cpp index 4b5eebea16..ed02b87391 100644 --- a/library/cpp/yt/string/unittests/guid_ut.cpp +++ b/library/cpp/yt/string/unittests/guid_ut.cpp @@ -10,7 +10,7 @@ namespace { //////////////////////////////////////////////////////////////////////////////// -static_assert(TFormatTraits<TGuid>::HasCustomFormatValue); +static_assert(CFormattable<TGuid>); TString CanonicalToString(TGuid value) { diff --git a/library/cpp/yt/yson_string/convert.cpp b/library/cpp/yt/yson_string/convert.cpp index 51a646f0ac..6d78ea6c9d 100644 --- a/library/cpp/yt/yson_string/convert.cpp +++ b/library/cpp/yt/yson_string/convert.cpp @@ -234,7 +234,8 @@ TString ParseStringFromYsonString(const TYsonStringBuf& str) throw TYsonLiteralParseException(ex, "Failed to decode string length"); } if (length < 0) { - throw TYsonLiteralParseException(Format("Negative string length ", + throw TYsonLiteralParseException(Format( + "Negative string length %v", length)); } if (static_cast<i64>(input.Avail()) != length) { diff --git a/library/cpp/yt/yson_string/string.cpp b/library/cpp/yt/yson_string/string.cpp index 164e450b72..ed2990a51b 100644 --- a/library/cpp/yt/yson_string/string.cpp +++ b/library/cpp/yt/yson_string/string.cpp @@ -197,14 +197,14 @@ void TYsonString::Load(IInputStream* s) //////////////////////////////////////////////////////////////////////////////// -TString ToString(const TYsonString& yson) +void FormatValue(TStringBuilderBase* builder, const TYsonString& yson, TStringBuf spec) { - return yson.ToString(); + FormatValue(builder, yson.ToString(), spec); } -TString ToString(const TYsonStringBuf& yson) +void FormatValue(TStringBuilderBase* builder, const TYsonStringBuf& yson, TStringBuf spec) { - return TString(yson.AsStringBuf()); + FormatValue(builder, yson.AsStringBuf(), spec); } //////////////////////////////////////////////////////////////////////////////// diff --git a/library/cpp/yt/yson_string/string.h b/library/cpp/yt/yson_string/string.h index d9f036569f..e310405ebc 100644 --- a/library/cpp/yt/yson_string/string.h +++ b/library/cpp/yt/yson_string/string.h @@ -4,6 +4,8 @@ #include <library/cpp/yt/memory/ref.h> +#include <library/cpp/yt/string/format.h> + #include <variant> namespace NYT::NYson { @@ -134,8 +136,8 @@ bool operator != (const TYsonString& lhs, const TYsonStringBuf& rhs); bool operator != (const TYsonStringBuf& lhs, const TYsonString& rhs); bool operator != (const TYsonStringBuf& lhs, const TYsonStringBuf& rhs); -TString ToString(const TYsonString& yson); -TString ToString(const TYsonStringBuf& yson); +void FormatValue(TStringBuilderBase* builder, const TYsonString& yson, TStringBuf spec); +void FormatValue(TStringBuilderBase* builder, const TYsonStringBuf& yson, TStringBuf spec); //////////////////////////////////////////////////////////////////////////////// diff --git a/yt/yt/client/chaos_client/replication_card.cpp b/yt/yt/client/chaos_client/replication_card.cpp index fa75f29ccb..dcafa48f63 100644 --- a/yt/yt/client/chaos_client/replication_card.cpp +++ b/yt/yt/client/chaos_client/replication_card.cpp @@ -111,11 +111,6 @@ void FormatValue(TStringBuilderBase* builder, const TReplicationCardFetchOptions options.IncludeHistory); } -TString ToString(const TReplicationCardFetchOptions& options) -{ - return ToStringViaBuilder(options); -} - bool TReplicationCardFetchOptions::Contains(const TReplicationCardFetchOptions& other) const { auto selfMask = NDetail::ToBitMask(*this); @@ -144,11 +139,6 @@ void FormatValue( } -TString ToString(const TReplicationProgress& replicationProgress) -{ - return ToStringViaBuilder(replicationProgress); -} - void FormatValue(TStringBuilderBase* builder, const TReplicaHistoryItem& replicaHistoryItem, TStringBuf /*spec*/) { builder->AppendFormat("{Era: %v, Timestamp: %v, Mode: %v, State: %v}", @@ -158,11 +148,6 @@ void FormatValue(TStringBuilderBase* builder, const TReplicaHistoryItem& replica replicaHistoryItem.State); } -TString ToString(const TReplicaHistoryItem& replicaHistoryItem) -{ - return ToStringViaBuilder(replicaHistoryItem); -} - void FormatValue( TStringBuilderBase* builder, const TReplicaInfo& replicaInfo, @@ -181,11 +166,6 @@ void FormatValue( builder->AppendFormat(", History: %v}", replicaInfo.History); } -TString ToString(const TReplicaInfo& replicaInfo) -{ - return ToStringViaBuilder(replicaInfo); -} - void FormatValue( TStringBuilderBase* builder, const TReplicationCard& replicationCard, diff --git a/yt/yt/client/chaos_client/replication_card.h b/yt/yt/client/chaos_client/replication_card.h index 62daba4e14..9581f887f7 100644 --- a/yt/yt/client/chaos_client/replication_card.h +++ b/yt/yt/client/chaos_client/replication_card.h @@ -96,7 +96,6 @@ struct TReplicationCardFetchOptions }; void FormatValue(TStringBuilderBase* builder, const TReplicationCardFetchOptions& options, TStringBuf /*spec*/); -TString ToString(const TReplicationCardFetchOptions& options); /////////////////////////////////////////////////////////////////////////////// @@ -105,17 +104,14 @@ void FormatValue( const TReplicationProgress& replicationProgress, TStringBuf /*spec*/, std::optional<TReplicationProgressProjection> replicationProgressProjection = std::nullopt); -TString ToString(const TReplicationProgress& replicationProgress); void FormatValue(TStringBuilderBase* builder, const TReplicaHistoryItem& replicaHistoryItem, TStringBuf /*spec*/); -TString ToString(const TReplicaHistoryItem& replicaHistoryItem); void FormatValue( TStringBuilderBase* builder, const TReplicaInfo& replicaInfo, TStringBuf /*spec*/, std::optional<TReplicationProgressProjection> replicationProgressProjection = std::nullopt); -TString ToString(const TReplicaInfo& replicaInfo); void FormatValue( TStringBuilderBase* builder, diff --git a/yt/yt/client/chunk_client/chunk_replica.cpp b/yt/yt/client/chunk_client/chunk_replica.cpp index a2262c04f3..4fe3c8335d 100644 --- a/yt/yt/client/chunk_client/chunk_replica.cpp +++ b/yt/yt/client/chunk_client/chunk_replica.cpp @@ -82,11 +82,6 @@ void FormatValue(TStringBuilderBase* builder, TChunkReplicaWithLocation replica, builder->AppendFormat("@%v", replica.GetChunkLocationUuid()); } -TString ToString(TChunkReplicaWithLocation replica) -{ - return ToStringViaBuilder(replica); -} - void FormatValue(TStringBuilderBase* builder, TChunkReplicaWithMedium replica, TStringBuf /*spec*/) { builder->AppendFormat("%v", replica.GetNodeId()); @@ -100,11 +95,6 @@ void FormatValue(TStringBuilderBase* builder, TChunkReplicaWithMedium replica, T } } -TString ToString(TChunkReplicaWithMedium replica) -{ - return ToStringViaBuilder(replica); -} - void FormatValue(TStringBuilderBase* builder, TChunkReplica replica, TStringBuf /*spec*/) { builder->AppendFormat("%v", replica.GetNodeId()); @@ -113,11 +103,6 @@ void FormatValue(TStringBuilderBase* builder, TChunkReplica replica, TStringBuf } } -TString ToString(TChunkReplica replica) -{ - return ToStringViaBuilder(replica); -} - void FormatValue(TStringBuilderBase* builder, const TChunkIdWithIndex& id, TStringBuf /*spec*/) { builder->AppendFormat("%v", id.Id); @@ -126,11 +111,6 @@ void FormatValue(TStringBuilderBase* builder, const TChunkIdWithIndex& id, TStri } } -TString ToString(const TChunkIdWithIndex& id) -{ - return ToStringViaBuilder(id); -} - void FormatValue(TStringBuilderBase* builder, const TChunkIdWithIndexes& id, TStringBuf /*spec*/) { builder->AppendFormat("%v", id.Id); @@ -144,11 +124,6 @@ void FormatValue(TStringBuilderBase* builder, const TChunkIdWithIndexes& id, TSt } } -TString ToString(const TChunkIdWithIndexes& id) -{ - return ToStringViaBuilder(id); -} - //////////////////////////////////////////////////////////////////////////////// TChunkReplicaAddressFormatter::TChunkReplicaAddressFormatter(TNodeDirectoryPtr nodeDirectory) diff --git a/yt/yt/client/chunk_client/chunk_replica.h b/yt/yt/client/chunk_client/chunk_replica.h index 11ff43d52c..520c4e964c 100644 --- a/yt/yt/client/chunk_client/chunk_replica.h +++ b/yt/yt/client/chunk_client/chunk_replica.h @@ -54,7 +54,6 @@ void ToProto(ui32* value, TChunkReplicaWithMedium replica) = delete; void FromProto(TChunkReplicaWithMedium* replica, ui32 value) = delete; void FormatValue(TStringBuilderBase* builder, TChunkReplicaWithMedium replica, TStringBuf spec); -TString ToString(TChunkReplicaWithMedium replica); //////////////////////////////////////////////////////////////////////////////// @@ -86,7 +85,6 @@ private: }; void FormatValue(TStringBuilderBase* builder, TChunkReplicaWithLocation replica, TStringBuf spec); -TString ToString(TChunkReplicaWithLocation replica); //////////////////////////////////////////////////////////////////////////////// @@ -115,7 +113,6 @@ private: }; void FormatValue(TStringBuilderBase* builder, TChunkReplica replica, TStringBuf spec); -TString ToString(TChunkReplica replica); //////////////////////////////////////////////////////////////////////////////// @@ -154,13 +151,13 @@ struct TChunkIdWithIndexes bool operator<(const TChunkIdWithIndex& lhs, const TChunkIdWithIndex& rhs); -TString ToString(const TChunkIdWithIndex& id); +void FormatValue(TStringBuilderBase* builder, const TChunkIdWithIndex& id, TStringBuf spec = {}); //////////////////////////////////////////////////////////////////////////////// bool operator<(const TChunkIdWithIndexes& lhs, const TChunkIdWithIndexes& rhs); -TString ToString(const TChunkIdWithIndexes& id); +void FormatValue(TStringBuilderBase* builder, const TChunkIdWithIndexes& id, TStringBuf spec = {}); //////////////////////////////////////////////////////////////////////////////// diff --git a/yt/yt/client/chunk_client/data_statistics.cpp b/yt/yt/client/chunk_client/data_statistics.cpp index 34327adbf7..1124abd672 100644 --- a/yt/yt/client/chunk_client/data_statistics.cpp +++ b/yt/yt/client/chunk_client/data_statistics.cpp @@ -211,18 +211,13 @@ TDuration TCodecStatistics::GetTotalDuration() const void FormatValue(TStringBuilderBase* builder, const TCodecStatistics& statistics, TStringBuf /* spec */) { - FormatKeyValueRange(builder, statistics.CodecToDuration(), TDefaultFormatter()); + ::NYT::FormatKeyValueRange(builder, statistics.CodecToDuration(), TDefaultFormatter()); if (statistics.ValueDictionaryCompressionDuration() != TDuration::Zero()) { builder->AppendFormat(", ValueDictionaryCompressionDuration: %v", statistics.ValueDictionaryCompressionDuration()); } } -TString ToString(const TCodecStatistics& statistics) -{ - return ToStringViaBuilder(statistics); -} - //////////////////////////////////////////////////////////////////////////////// } // namespace NYT::NChunkClient diff --git a/yt/yt/client/chunk_client/data_statistics.h b/yt/yt/client/chunk_client/data_statistics.h index 0a8b918627..9a9cbed0a3 100644 --- a/yt/yt/client/chunk_client/data_statistics.h +++ b/yt/yt/client/chunk_client/data_statistics.h @@ -12,6 +12,8 @@ #include <library/cpp/yt/small_containers/compact_flat_map.h> +#include <library/cpp/yt/string/format.h> + namespace NYT::NChunkClient { //////////////////////////////////////////////////////////////////////////////// @@ -69,7 +71,6 @@ private: }; void FormatValue(TStringBuilderBase* builder, const TCodecStatistics& statistics, TStringBuf spec); -TString ToString(const TCodecStatistics& statistics); //////////////////////////////////////////////////////////////////////////////// diff --git a/yt/yt/client/chunk_client/read_limit.cpp b/yt/yt/client/chunk_client/read_limit.cpp index 3d10604388..e862c18a54 100644 --- a/yt/yt/client/chunk_client/read_limit.cpp +++ b/yt/yt/client/chunk_client/read_limit.cpp @@ -288,6 +288,11 @@ TString ToString(const TLegacyReadLimit& limit) return builder.Flush(); } +void FormatValue(TStringBuilderBase* builder, const TLegacyReadLimit& limit, TStringBuf spec) +{ + FormatValue(builder, ToString(limit), spec); +} + bool IsTrivial(const TLegacyReadLimit& limit) { return limit.IsTrivial(); @@ -438,9 +443,9 @@ void TLegacyReadRange::InitMove(NProto::TReadRange&& range) //////////////////////////////////////////////////////////////////////////////// -TString ToString(const TLegacyReadRange& range) +void FormatValue(TStringBuilderBase* builder, const TLegacyReadRange& range, TStringBuf spec) { - return Format("[<%v> : <%v>]", range.LowerLimit(), range.UpperLimit()); + FormatValue(builder, Format("[<%v> : <%v>]", range.LowerLimit(), range.UpperLimit()), spec); } void ToProto(NProto::TReadRange* protoReadRange, const TLegacyReadRange& readRange) @@ -725,6 +730,11 @@ TString ToString(const TReadLimit& readLimit) return builder.Flush(); } +void FormatValue(TStringBuilderBase* builder, const TReadLimit& readLimit, TStringBuf spec) +{ + FormatValue(builder, ToString(readLimit), spec); +} + void ToProto(NProto::TReadLimit* protoReadLimit, const TReadLimit& readLimit) { if (readLimit.KeyBound()) { @@ -888,9 +898,9 @@ TReadRange TReadRange::MakeEmpty() //////////////////////////////////////////////////////////////////////////////// -TString ToString(const TReadRange& readRange) +void FormatValue(TStringBuilderBase* builder, const TReadRange& readRange, TStringBuf spec) { - return Format("[<%v> : <%v>]", readRange.LowerLimit(), readRange.UpperLimit()); + FormatValue(builder, Format("[<%v> : <%v>]", readRange.LowerLimit(), readRange.UpperLimit()), spec); } void ToProto(NProto::TReadRange* protoReadRange, const TReadRange& readRange) diff --git a/yt/yt/client/chunk_client/read_limit.h b/yt/yt/client/chunk_client/read_limit.h index 0ce33e3b0e..5faa08a654 100644 --- a/yt/yt/client/chunk_client/read_limit.h +++ b/yt/yt/client/chunk_client/read_limit.h @@ -81,6 +81,7 @@ private: //////////////////////////////////////////////////////////////////////////////// TString ToString(const TLegacyReadLimit& limit); +void FormatValue(TStringBuilderBase* builder, const TLegacyReadLimit& limit, TStringBuf spec); bool IsTrivial(const TLegacyReadLimit& limit); bool IsTrivial(const NProto::TReadLimit& limit); @@ -117,7 +118,7 @@ private: //////////////////////////////////////////////////////////////////////////////// -TString ToString(const TLegacyReadRange& range); +void FormatValue(TStringBuilderBase* builder, const TLegacyReadRange& range, TStringBuf spec); void ToProto(NProto::TReadRange* protoReadRange, const TLegacyReadRange& readRange); void FromProto(TLegacyReadRange* readRange, const NProto::TReadRange& protoReadRange); @@ -180,6 +181,7 @@ public: //////////////////////////////////////////////////////////////////////////////// TString ToString(const TReadLimit& readLimit); +void FormatValue(TStringBuilderBase* builder, const TReadLimit& readLimit, TStringBuf spec); void ToProto(NProto::TReadLimit* protoReadLimit, const TReadLimit& readLimit); //! If protoReadLimit contains key, it is transformed into new key bound by @@ -214,7 +216,7 @@ public: //////////////////////////////////////////////////////////////////////////////// -TString ToString(const TReadRange& readRange); +void FormatValue(TStringBuilderBase* builder, const TReadRange& readRange, TStringBuf spec); void ToProto(NProto::TReadRange* protoReadRange, const TReadRange& readRange); //! See comment for FromProto(TReadLimit*, const NProto::TReadLimit&, bool, std::optional<int>). diff --git a/yt/yt/client/election/public.h b/yt/yt/client/election/public.h index 7b79682b48..baf25c8772 100644 --- a/yt/yt/client/election/public.h +++ b/yt/yt/client/election/public.h @@ -5,6 +5,8 @@ #include <library/cpp/yt/misc/enum.h> #include <library/cpp/yt/misc/guid.h> +#include <library/cpp/yt/string/guid.h> + namespace NYT::NElection { //////////////////////////////////////////////////////////////////////////////// diff --git a/yt/yt/client/query_client/query_statistics.cpp b/yt/yt/client/query_client/query_statistics.cpp index fd2b30b651..1ae041a995 100644 --- a/yt/yt/client/query_client/query_statistics.cpp +++ b/yt/yt/client/query_client/query_statistics.cpp @@ -59,9 +59,10 @@ void FromProto(TQueryStatistics* original, const NProto::TQueryStatistics& seria FromProto(&original->InnerStatistics, serialized.inner_statistics()); } -TString ToString(const TQueryStatistics& stats) +void FormatValue(TStringBuilderBase* builder, const TQueryStatistics& stats, TStringBuf /*spec*/) { - return Format( + Format( + builder, "{" "RowsRead: %v, DataWeightRead: %v, RowsWritten: %v, " "SyncTime: %v, AsyncTime: %v, ExecuteTime: %v, ReadTime: %v, WriteTime: %v, CodegenTime: %v, " diff --git a/yt/yt/client/query_client/query_statistics.h b/yt/yt/client/query_client/query_statistics.h index 7f9464e561..a759a09372 100644 --- a/yt/yt/client/query_client/query_statistics.h +++ b/yt/yt/client/query_client/query_statistics.h @@ -32,7 +32,7 @@ struct TQueryStatistics void ToProto(NProto::TQueryStatistics* serialized, const TQueryStatistics& original); void FromProto(TQueryStatistics* original, const NProto::TQueryStatistics& serialized); -TString ToString(const TQueryStatistics& stat); +void FormatValue(TStringBuilderBase* builder, const TQueryStatistics& stat, TStringBuf spec); void Serialize(const TQueryStatistics& statistics, NYson::IYsonConsumer* consumer); diff --git a/yt/yt/client/queue_client/common.cpp b/yt/yt/client/queue_client/common.cpp index 22beeb98bb..d054f2c34c 100644 --- a/yt/yt/client/queue_client/common.cpp +++ b/yt/yt/client/queue_client/common.cpp @@ -60,9 +60,9 @@ TCrossClusterReference TCrossClusterReference::FromRichYPath(const TRichYPath& p }; } -TString ToString(const TCrossClusterReference& queueRef) +void FormatValue(TStringBuilderBase* builder, const TCrossClusterReference& queueRef, TStringBuf spec) { - return Format("%v:%v", queueRef.Cluster, queueRef.Path); + FormatValue(builder, Format("%v:%v", queueRef.Cluster, queueRef.Path), spec); } void Serialize(const TCrossClusterReference& queueRef, IYsonConsumer* consumer) diff --git a/yt/yt/client/queue_client/common.h b/yt/yt/client/queue_client/common.h index 93e5bc409e..9403017410 100644 --- a/yt/yt/client/queue_client/common.h +++ b/yt/yt/client/queue_client/common.h @@ -25,7 +25,7 @@ struct TCrossClusterReference static TCrossClusterReference FromRichYPath(const NYPath::TRichYPath& path); }; -TString ToString(const TCrossClusterReference& queueRef); +void FormatValue(TStringBuilderBase* builder, const TCrossClusterReference& queueRef, TStringBuf spec); void Serialize(const TCrossClusterReference& queueRef, NYson::IYsonConsumer* consumer); diff --git a/yt/yt/client/table_client/config.cpp b/yt/yt/client/table_client/config.cpp index 32a8317feb..1ed6989885 100644 --- a/yt/yt/client/table_client/config.cpp +++ b/yt/yt/client/table_client/config.cpp @@ -20,12 +20,14 @@ using namespace NChunkClient; //////////////////////////////////////////////////////////////////////////////// -TString ToString(const TRetentionConfigPtr& obj) +void FormatValue(TStringBuilderBase* builder, const TRetentionConfigPtr& obj, TStringBuf spec) { - static const TString NullPtrName("<nullptr>"); - return obj - ? NYson::ConvertToYsonString(obj, NYson::EYsonFormat::Text).ToString() - : NullPtrName; + static const TStringBuf NullPtrName("<nullptr>"); + if (!obj) { + FormatValue(builder, NullPtrName, spec); + return; + } + FormatValue(builder, NYson::ConvertToYsonString(obj, NYson::EYsonFormat::Text), spec); } //////////////////////////////////////////////////////////////////////////////// diff --git a/yt/yt/client/table_client/config.h b/yt/yt/client/table_client/config.h index 56e4ccfa1c..9423012fa4 100644 --- a/yt/yt/client/table_client/config.h +++ b/yt/yt/client/table_client/config.h @@ -33,7 +33,7 @@ public: DEFINE_REFCOUNTED_TYPE(TRetentionConfig) -TString ToString(const TRetentionConfigPtr& obj); +void FormatValue(TStringBuilderBase* builder, const TRetentionConfigPtr& obj, TStringBuf spec); //////////////////////////////////////////////////////////////////////////////// diff --git a/yt/yt/client/table_client/logical_type.cpp b/yt/yt/client/table_client/logical_type.cpp index 2fec0be07e..4a677262fa 100644 --- a/yt/yt/client/table_client/logical_type.cpp +++ b/yt/yt/client/table_client/logical_type.cpp @@ -286,6 +286,12 @@ TString ToString(const TLogicalType& logicalType) YT_ABORT(); } +void FormatValue(TStringBuilderBase* builder, const TLogicalType& logicalType, TStringBuf spec) +{ + // TODO(arkady-e1ppa): Optimize and express ToString using this. + FormatValue(builder, ToString(logicalType), spec); +} + void PrintTo(ELogicalMetatype metatype, std::ostream* os) { *os << ToString(metatype); diff --git a/yt/yt/client/table_client/logical_type.h b/yt/yt/client/table_client/logical_type.h index 63750956f2..b7db1a8143 100644 --- a/yt/yt/client/table_client/logical_type.h +++ b/yt/yt/client/table_client/logical_type.h @@ -104,6 +104,7 @@ private: DEFINE_REFCOUNTED_TYPE(TLogicalType) TString ToString(const TLogicalType& logicalType); +void FormatValue(TStringBuilderBase* builder, const TLogicalType& logicalType, TStringBuf /*spec*/); //! Debug printers for Gtest unittests. void PrintTo(ELogicalMetatype type, std::ostream* os); diff --git a/yt/yt/client/table_client/row_base.cpp b/yt/yt/client/table_client/row_base.cpp index b2970d58d6..16703b8fc3 100644 --- a/yt/yt/client/table_client/row_base.cpp +++ b/yt/yt/client/table_client/row_base.cpp @@ -168,12 +168,12 @@ void ValidateColumnFilter(const TColumnFilter& columnFilter, int schemaColumnCou //////////////////////////////////////////////////////////////////////////////// -TString ToString(const TColumnFilter& columnFilter) +void FormatValue(TStringBuilderBase* builder, const TColumnFilter& columnFilter, TStringBuf spec) { if (columnFilter.IsUniversal()) { - return TString("{All}"); + FormatValue(builder, TStringBuf{"{All}"}, spec); } else { - return Format("{%v}", columnFilter.GetIndexes()); + FormatValue(builder, Format("{%v}", columnFilter.GetIndexes()), spec); } } diff --git a/yt/yt/client/table_client/row_base.h b/yt/yt/client/table_client/row_base.h index fcbb675a39..1907e7082f 100644 --- a/yt/yt/client/table_client/row_base.h +++ b/yt/yt/client/table_client/row_base.h @@ -336,7 +336,7 @@ private: //////////////////////////////////////////////////////////////////////////////// -TString ToString(const TColumnFilter& columnFilter); +void FormatValue(TStringBuilderBase* builder, const TColumnFilter& columnFilter, TStringBuf spec); //////////////////////////////////////////////////////////////////////////////// diff --git a/yt/yt/client/table_client/schema.cpp b/yt/yt/client/table_client/schema.cpp index d815e0d58e..1e3637fb0b 100644 --- a/yt/yt/client/table_client/schema.cpp +++ b/yt/yt/client/table_client/schema.cpp @@ -1376,11 +1376,6 @@ void FormatValue(TStringBuilderBase* builder, const TTableSchema& schema, TStrin builder->AppendChar(']'); } -TString ToString(const TTableSchema& schema) -{ - return ToStringViaBuilder(schema); -} - void FormatValue(TStringBuilderBase* builder, const TTableSchemaPtr& schema, TStringBuf spec) { if (schema) { @@ -1390,11 +1385,6 @@ void FormatValue(TStringBuilderBase* builder, const TTableSchemaPtr& schema, TSt } } -TString ToString(const TTableSchemaPtr& schema) -{ - return ToStringViaBuilder(schema); -} - TString SerializeToWireProto(const TTableSchemaPtr& schema) { NTableClient::NProto::TTableSchemaExt protoSchema; diff --git a/yt/yt/client/table_client/schema.h b/yt/yt/client/table_client/schema.h index 9c6e794072..d5bb5a37aa 100644 --- a/yt/yt/client/table_client/schema.h +++ b/yt/yt/client/table_client/schema.h @@ -422,9 +422,6 @@ DEFINE_REFCOUNTED_TYPE(TTableSchema) void FormatValue(TStringBuilderBase* builder, const TTableSchema& schema, TStringBuf spec); void FormatValue(TStringBuilderBase* builder, const TTableSchemaPtr& schema, TStringBuf spec); -TString ToString(const TTableSchema& schema); -TString ToString(const TTableSchemaPtr& schema); - //! Returns serialized NTableClient.NProto.TTableSchemaExt. TString SerializeToWireProto(const TTableSchemaPtr& schema); diff --git a/yt/yt/client/ypath/rich.cpp b/yt/yt/client/ypath/rich.cpp index ab78d81171..2336220f63 100644 --- a/yt/yt/client/ypath/rich.cpp +++ b/yt/yt/client/ypath/rich.cpp @@ -695,6 +695,11 @@ TString ToString(const TRichYPath& path) return ConvertToString(path, EYsonFormat::Text); } +void FormatValue(TStringBuilderBase* builder, const TRichYPath& path, TStringBuf spec) +{ + FormatValue(builder, ToString(path), spec); +} + std::vector<TRichYPath> Normalize(const std::vector<TRichYPath>& paths) { std::vector<TRichYPath> result; diff --git a/yt/yt/client/ypath/rich.h b/yt/yt/client/ypath/rich.h index d74f0609c6..e1dc440db9 100644 --- a/yt/yt/client/ypath/rich.h +++ b/yt/yt/client/ypath/rich.h @@ -177,6 +177,7 @@ bool operator== (const TRichYPath& lhs, const TRichYPath& rhs); //////////////////////////////////////////////////////////////////////////////// TString ToString(const TRichYPath& path); +void FormatValue(TStringBuilderBase* builder, const TRichYPath& path, TStringBuf spec); std::vector<TRichYPath> Normalize(const std::vector<TRichYPath>& paths); diff --git a/yt/yt/core/concurrency/fiber.cpp b/yt/yt/core/concurrency/fiber.cpp index 3c7b72b5f5..1b0717fa26 100644 --- a/yt/yt/core/concurrency/fiber.cpp +++ b/yt/yt/core/concurrency/fiber.cpp @@ -233,7 +233,6 @@ private: auto* regNode = static_cast<TIntrusiveListItem<NDetail::TFiberBase, NDetail::TFiberRegisterTag>*>(fiber); auto* delNode = static_cast<TIntrusiveListItem<NDetail::TFiberBase, NDetail::TFiberUnregisterTag>*>(fiber); - Cerr << Format("Fiber node at %v", iter) << '\n'; Cerr << Format("Fiber address after cast is %v", fiber) << '\n'; Cerr << Format("Fiber registration queue status: Next: %v, Prev: %v", regNode->Next(), regNode->Prev()) << '\n'; // NB: Reading deletion queue is data race. Don't do this under tsan. diff --git a/yt/yt/core/logging/file_log_writer.cpp b/yt/yt/core/logging/file_log_writer.cpp index 89d9b50d93..371866c55a 100644 --- a/yt/yt/core/logging/file_log_writer.cpp +++ b/yt/yt/core/logging/file_log_writer.cpp @@ -286,7 +286,7 @@ private: int width = ToString(ssize(fileNames)).length(); TString formatString = "%v.%0" + ToString(width) + "d"; for (int index = ssize(fileNames); index > 0; --index) { - auto newFileName = Format(formatString, FileNamePrefix_, index); + auto newFileName = Format(TRuntimeFormat{formatString}, FileNamePrefix_, index); auto oldPath = NFS::CombinePaths(DirectoryName_, fileNames[index - 1]); auto newPath = NFS::CombinePaths(DirectoryName_, newFileName); YT_LOG_DEBUG("Rename log segment (OldFilePath: %v, NewFilePath: %v)", oldPath, newPath); diff --git a/yt/yt/core/misc/error-inl.h b/yt/yt/core/misc/error-inl.h index d147a3dd9a..79dc4108f9 100644 --- a/yt/yt/core/misc/error-inl.h +++ b/yt/yt/core/misc/error-inl.h @@ -55,7 +55,7 @@ namespace NDetail { template <size_t Length, class... TArgs> TString FormatErrorMessage(const char (&format)[Length], TArgs&&... args) { - return Format(format, std::forward<TArgs>(args)...); + return Format(TRuntimeFormat{format}, std::forward<TArgs>(args)...); } template <size_t Length> diff --git a/yt/yt/core/misc/error_code.cpp b/yt/yt/core/misc/error_code.cpp index 7178d24803..f713cc9c8a 100644 --- a/yt/yt/core/misc/error_code.cpp +++ b/yt/yt/core/misc/error_code.cpp @@ -146,17 +146,24 @@ TString TErrorCodeRegistry::ParseNamespace(const std::type_info& errorCodeEnumTy return name; } -TString ToString(const TErrorCodeRegistry::TErrorCodeInfo& errorCodeInfo) +void FormatValue( + TStringBuilderBase* builder, + const TErrorCodeRegistry::TErrorCodeInfo& errorCodeInfo, + TStringBuf /*spec*/) { if (errorCodeInfo.Namespace.empty()) { - return Format("EErrorCode::%v", errorCodeInfo.Name); + Format(builder, "EErrorCode::%v", errorCodeInfo.Name); + return; } - return Format("%v::EErrorCode::%v", errorCodeInfo.Namespace, errorCodeInfo.Name); + Format(builder, "%v::EErrorCode::%v", errorCodeInfo.Namespace, errorCodeInfo.Name); } -TString ToString(const TErrorCodeRegistry::TErrorCodeRangeInfo& errorCodeRangeInfo) +void FormatValue( + TStringBuilderBase* builder, + const TErrorCodeRegistry::TErrorCodeRangeInfo& errorCodeRangeInfo, + TStringBuf /*spec*/) { - return Format("%v-%v", errorCodeRangeInfo.From, errorCodeRangeInfo.To); + Format(builder, "%v-%v", errorCodeRangeInfo.From, errorCodeRangeInfo.To); } //////////////////////////////////////////////////////////////////////////////// diff --git a/yt/yt/core/misc/error_code.h b/yt/yt/core/misc/error_code.h index 3ab42c092a..2ab243522e 100644 --- a/yt/yt/core/misc/error_code.h +++ b/yt/yt/core/misc/error_code.h @@ -3,6 +3,8 @@ #include <library/cpp/yt/misc/enum.h> #include <library/cpp/yt/misc/port.h> +#include <library/cpp/yt/string/format.h> + #include <util/generic/hash_set.h> #include <library/cpp/yt/misc/preprocessor.h> @@ -61,8 +63,15 @@ private: void CheckCodesAgainstRanges() const; }; -TString ToString(const TErrorCodeRegistry::TErrorCodeInfo& errorCodeInfo); -TString ToString(const TErrorCodeRegistry::TErrorCodeRangeInfo& errorCodeInfo); +void FormatValue( + TStringBuilderBase* builder, + const TErrorCodeRegistry::TErrorCodeInfo& errorCodeInfo, + TStringBuf spec); + +void FormatValue( + TStringBuilderBase* builder, + const TErrorCodeRegistry::TErrorCodeRangeInfo& errorCodeInfo, + TStringBuf spec); //////////////////////////////////////////////////////////////////////////////// diff --git a/yt/yt/core/misc/protobuf_helpers-inl.h b/yt/yt/core/misc/protobuf_helpers-inl.h index 2dd1757eea..adf5a30851 100644 --- a/yt/yt/core/misc/protobuf_helpers-inl.h +++ b/yt/yt/core/misc/protobuf_helpers-inl.h @@ -572,28 +572,6 @@ i64 TRefCountedProto<TProto>::GetSize() const //////////////////////////////////////////////////////////////////////////////// -// RepeatedField formatter -template <class T> -struct TValueFormatter<::google::protobuf::RepeatedField<T>> -{ - static void Do(TStringBuilderBase* builder, const ::google::protobuf::RepeatedField<T>& collection, TStringBuf /*format*/) - { - FormatRange(builder, collection, TDefaultFormatter()); - } -}; - -// RepeatedPtrField formatter -template <class T> -struct TValueFormatter<::google::protobuf::RepeatedPtrField<T>> -{ - static void Do(TStringBuilderBase* builder, const ::google::protobuf::RepeatedPtrField<T>& collection, TStringBuf /*format*/) - { - FormatRange(builder, collection, TDefaultFormatter()); - } -}; - -//////////////////////////////////////////////////////////////////////////////// - template <class TSerialized, class T, class TTag> void FromProto(TStrongTypedef<T, TTag>* original, const TSerialized& serialized) { @@ -608,4 +586,38 @@ void ToProto(TSerialized* serialized, const TStrongTypedef<T, TTag>& original) //////////////////////////////////////////////////////////////////////////////// +template <std::derived_from<::google::protobuf::MessageLite> T> +void FormatValue(TStringBuilderBase* builder, const T& message, TStringBuf spec) +{ + FormatValue(builder, NYT::ToStringIgnoringFormatValue(message), spec); +} + +//////////////////////////////////////////////////////////////////////////////// + } // namespace NYT + +namespace google::protobuf { + +//////////////////////////////////////////////////////////////////////////////// + +template <class T> +void FormatValue( + NYT::TStringBuilderBase* builder, + const ::google::protobuf::RepeatedField<T>& collection, + TStringBuf /*spec*/) +{ + NYT::FormatRange(builder, collection, NYT::TDefaultFormatter()); +} + +template <class T> +void FormatValue( + NYT::TStringBuilderBase* builder, + const ::google::protobuf::RepeatedPtrField<T>& collection, + TStringBuf /*spec*/) +{ + NYT::FormatRange(builder, collection, NYT::TDefaultFormatter()); +} + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace google::protobuf diff --git a/yt/yt/core/misc/protobuf_helpers.h b/yt/yt/core/misc/protobuf_helpers.h index 70e163c7a7..c20ace8017 100644 --- a/yt/yt/core/misc/protobuf_helpers.h +++ b/yt/yt/core/misc/protobuf_helpers.h @@ -214,6 +214,11 @@ TSharedRef PopEnvelope(const TSharedRef& data); //////////////////////////////////////////////////////////////////////////////// +template <std::derived_from<::google::protobuf::MessageLite> T> +void FormatValue(TStringBuilderBase* builder, const T& message, TStringBuf /*spec*/); + +//////////////////////////////////////////////////////////////////////////////// + struct TBinaryProtoSerializer { //! Serializes a given protobuf message into a given stream. @@ -466,6 +471,26 @@ public: } // namespace NYT +namespace google::protobuf { + +//////////////////////////////////////////////////////////////////////////////// + +template <class T> +void FormatValue( + NYT::TStringBuilderBase* builder, + const ::google::protobuf::RepeatedField<T>& collection, + TStringBuf /*spec*/); + +template <class T> +void FormatValue( + NYT::TStringBuilderBase* builder, + const ::google::protobuf::RepeatedPtrField<T>& collection, + TStringBuf /*spec*/); + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace google::protobuf + #define PROTOBUF_HELPERS_INL_H_ #include "protobuf_helpers-inl.h" #undef PROTOBUF_HELPERS_INL_H_ diff --git a/yt/yt/core/misc/range_formatters-inl.h b/yt/yt/core/misc/range_formatters-inl.h new file mode 100644 index 0000000000..7dba39874f --- /dev/null +++ b/yt/yt/core/misc/range_formatters-inl.h @@ -0,0 +1,25 @@ +#ifndef RANGE_FORMATTERS_INL_H_ +#error "Direct inclusion of this file is not allowed, include range_formatters.h" +// For the sake of sane code completion. +#include "range_formatters.h" +#endif + +namespace NYT { + +//////////////////////////////////////////////////////////////////////////////// + +template <class T> +void FormatValue(TStringBuilderBase* builder, const TRange<T>& collection, TStringBuf /*spec*/) +{ + NYT::FormatRange(builder, collection, TDefaultFormatter()); +} + +template <class T> +void FormatValue(TStringBuilderBase* builder, const TSharedRange<T>& collection, TStringBuf /*spec*/) +{ + NYT::FormatRange(builder, collection, TDefaultFormatter()); +} + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT diff --git a/yt/yt/core/misc/range_formatters.h b/yt/yt/core/misc/range_formatters.h index 4bfdd13a2c..6cdfa33bf9 100644 --- a/yt/yt/core/misc/range_formatters.h +++ b/yt/yt/core/misc/range_formatters.h @@ -9,26 +9,16 @@ namespace NYT { //////////////////////////////////////////////////////////////////////////////// -// TRange formatter template <class T> -struct TValueFormatter<TRange<T>, void> -{ - static void Do(TStringBuilderBase* builder, TRange<T> range, TStringBuf /*format*/) - { - FormatRange(builder, range, TDefaultFormatter()); - } -}; - -// TSharedRange formatter +void FormatValue(TStringBuilderBase* builder, const TRange<T>& collection, TStringBuf /*spec*/); + template <class T> -struct TValueFormatter<TSharedRange<T>> -{ - static void Do(TStringBuilderBase* builder, const TSharedRange<T>& range, TStringBuf /*format*/) - { - FormatRange(builder, range, TDefaultFormatter()); - } -}; +void FormatValue(TStringBuilderBase* builder, const TSharedRange<T>& collection, TStringBuf /*spec*/); //////////////////////////////////////////////////////////////////////////////// } // namespace NYT + +#define RANGE_FORMATTERS_INL_H_ +#include "range_formatters-inl.h" +#undef RANGE_FORMATTERS_INL_H_ diff --git a/yt/yt/core/misc/serialize_dump.h b/yt/yt/core/misc/serialize_dump.h index 7581032bee..6a01444638 100644 --- a/yt/yt/core/misc/serialize_dump.h +++ b/yt/yt/core/misc/serialize_dump.h @@ -184,7 +184,7 @@ struct TSerializationDumpPodWriter template <class C> static void Do(C& context, const T& value) { - if constexpr(TFormatTraits<T>::HasCustomFormatValue) { + if constexpr(CFormattable<T>) { SERIALIZATION_DUMP_WRITE(context, "pod %v", value); } else { SERIALIZATION_DUMP_WRITE(context, "pod[%v] %v", sizeof(T), DumpRangeToHex(TRef::FromPod(value))); diff --git a/yt/yt/core/misc/unittests/format_ut.cpp b/yt/yt/core/misc/unittests/format_ut.cpp index cfbce43656..d8c7834e05 100644 --- a/yt/yt/core/misc/unittests/format_ut.cpp +++ b/yt/yt/core/misc/unittests/format_ut.cpp @@ -9,7 +9,7 @@ namespace { //////////////////////////////////////////////////////////////////////////////// -static_assert(!TFormatTraits<TIntrusivePtr<TRefCounted>>::HasCustomFormatValue); +static_assert(!CFormattable<TIntrusivePtr<TRefCounted>>); //////////////////////////////////////////////////////////////////////////////// diff --git a/yt/yt/core/net/address.cpp b/yt/yt/core/net/address.cpp index 2f05fe40df..3044ddeb4f 100644 --- a/yt/yt/core/net/address.cpp +++ b/yt/yt/core/net/address.cpp @@ -388,6 +388,12 @@ void FromProto(TNetworkAddress* address, const TString& protoAddress) address->Length_ = protoAddress.size(); } +void FormatValue(TStringBuilderBase* builder, const TNetworkAddress& address, TStringBuf spec) +{ + // TODO(arkady-e1ppa): Optimize. + FormatValue(builder, ToString(address), spec); +} + TString ToString(const TNetworkAddress& address, const TNetworkAddressFormatOptions& options) { const auto& sockAddr = address.GetSockAddr(); diff --git a/yt/yt/core/net/address.h b/yt/yt/core/net/address.h index 37626d5d23..9e139c5af4 100644 --- a/yt/yt/core/net/address.h +++ b/yt/yt/core/net/address.h @@ -93,6 +93,7 @@ struct TNetworkAddressFormatOptions bool IncludeTcpProtocol = true; }; +void FormatValue(TStringBuilderBase* builder, const TNetworkAddress& address, TStringBuf spec); TString ToString(const TNetworkAddress& address, const TNetworkAddressFormatOptions& options = {}); bool operator == (const TNetworkAddress& lhs, const TNetworkAddress& rhs); diff --git a/yt/yt/core/rpc/service-inl.h b/yt/yt/core/rpc/service-inl.h index 501bf06a66..3b60e42e72 100644 --- a/yt/yt/core/rpc/service-inl.h +++ b/yt/yt/core/rpc/service-inl.h @@ -14,7 +14,7 @@ template <class... TArgs> void IServiceContext::SetRequestInfo(const char* format, TArgs&&... args) { if (IsLoggingEnabled()) { - SetRawRequestInfo(Format(format, std::forward<TArgs>(args)...), /*incremental*/ false); + SetRawRequestInfo(Format(TRuntimeFormat{format}, std::forward<TArgs>(args)...), /*incremental*/ false); } else { SuppressMissingRequestInfoCheck(); } @@ -24,7 +24,7 @@ template <class... TArgs> void IServiceContext::SetIncrementalRequestInfo(const char* format, TArgs&&... args) { if (IsLoggingEnabled()) { - SetRawRequestInfo(Format(format, std::forward<TArgs>(args)...), /*incremental*/ true); + SetRawRequestInfo(Format(TRuntimeFormat{format}, std::forward<TArgs>(args)...), /*incremental*/ true); } else { SuppressMissingRequestInfoCheck(); } @@ -34,7 +34,7 @@ template <class... TArgs> void IServiceContext::SetResponseInfo(const char* format, TArgs&&... args) { if (IsLoggingEnabled()) { - SetRawResponseInfo(Format(format, std::forward<TArgs>(args)...), /*incremental*/ false); + SetRawResponseInfo(Format(TRuntimeFormat{format}, std::forward<TArgs>(args)...), /*incremental*/ false); } } @@ -42,7 +42,7 @@ template <class... TArgs> void IServiceContext::SetIncrementalResponseInfo(const char* format, TArgs&&... args) { if (IsLoggingEnabled()) { - SetRawResponseInfo(Format(format, std::forward<TArgs>(args)...), /*incremental*/ true); + SetRawResponseInfo(Format(TRuntimeFormat{format}, std::forward<TArgs>(args)...), /*incremental*/ true); } } diff --git a/yt/yt/core/yson/token.cpp b/yt/yt/core/yson/token.cpp index 7d486d83c7..3a8ad50964 100644 --- a/yt/yt/core/yson/token.cpp +++ b/yt/yt/core/yson/token.cpp @@ -205,29 +205,37 @@ void TToken::Reset() BooleanValue_ = false; } -TString ToString(const TToken& token) +// TODO(arkady-e1ppa): Consider doing ToString anyway (e.g. with spec = 'v') +// and then applying spec to the string value. +void FormatValue(TStringBuilderBase* builder, const TToken& token, TStringBuf spec) { switch (token.GetType()) { case ETokenType::EndOfStream: - return TString(); + FormatValue(builder, TStringBuf{}, spec); + break; case ETokenType::String: - return TString(token.GetStringValue()); + FormatValue(builder, token.GetStringValue(), spec); + break; case ETokenType::Int64: - return ::ToString(token.GetInt64Value()); + FormatValue(builder, token.GetInt64Value(), spec); + break; case ETokenType::Uint64: - return ::ToString(token.GetUint64Value()); + FormatValue(builder, token.GetUint64Value(), spec); + break; case ETokenType::Double: - return ::ToString(token.GetDoubleValue()); + FormatValue(builder, token.GetDoubleValue(), spec); + break; case ETokenType::Boolean: - return TString(FormatBool(token.GetBooleanValue())); + FormatValue(builder, FormatBool(token.GetBooleanValue()), spec); + break; default: - return TokenTypeToString(token.GetType()); + FormatValue(builder, TokenTypeToString(token.GetType()), spec); } } diff --git a/yt/yt/core/yson/token.h b/yt/yt/core/yson/token.h index c9da4c9272..66c3e91075 100644 --- a/yt/yt/core/yson/token.h +++ b/yt/yt/core/yson/token.h @@ -84,7 +84,7 @@ private: bool BooleanValue_ = false; }; -TString ToString(const TToken& token); +void FormatValue(TStringBuilderBase* builder, const TToken& token, TStringBuf spec); //////////////////////////////////////////////////////////////////////////////// diff --git a/yt/yt/library/numeric/serialize/double_array.h b/yt/yt/library/numeric/serialize/double_array.h index a700b4d0ab..9991d81431 100644 --- a/yt/yt/library/numeric/serialize/double_array.h +++ b/yt/yt/library/numeric/serialize/double_array.h @@ -9,19 +9,17 @@ namespace NYT { //////////////////////////////////////////////////////////////////////////////// template <class TDerived> -struct TValueFormatter<TDerived, std::enable_if_t<IsDoubleArray<TDerived>>> + requires (IsDoubleArray<TDerived>) +void FormatValue(TStringBuilderBase* builder, const TDerived& vec, TStringBuf spec) { - static void Do(TStringBuilderBase* builder, const TDerived& vec, TStringBuf format) - { - builder->AppendChar('['); - FormatValue(builder, vec[0], format); - for (size_t i = 1; i < TDerived::Size; i++) { - builder->AppendChar(' '); - FormatValue(builder, vec[i], format); - } - builder->AppendChar(']'); + builder->AppendChar('['); + FormatValue(builder, vec[0], spec); + for (size_t i = 1; i < TDerived::Size; i++) { + builder->AppendChar(' '); + FormatValue(builder, vec[i], spec); } -}; + builder->AppendChar(']'); +} // TODO(ignat) // template <class TDerived, class = std::enable_if_t<IsDoubleArray<TDerived>>> diff --git a/yt/yt/library/process/pipe.cpp b/yt/yt/library/process/pipe.cpp index 6584b75b5d..e4051f6604 100644 --- a/yt/yt/library/process/pipe.cpp +++ b/yt/yt/library/process/pipe.cpp @@ -208,11 +208,20 @@ void TPipe::CloseWriteFD() //////////////////////////////////////////////////////////////////////////////// -TString ToString(const TPipe& pipe) +void FormatValue(TStringBuilderBase* builder, const TPipe& pipe, TStringBuf spec) { - return Format("{ReadFD: %v, WriteFD: %v}", - pipe.GetReadFD(), - pipe.GetWriteFD()); + // TODO(arkady-e1ppa): We format pipe twice + // (pipe itself and its serialization) + // This is probably redundant. + // Check if it is later and remove + // the second step. + FormatValue( + builder, + Format( + "{ReadFD: %v, WriteFD: %v}", + pipe.GetReadFD(), + pipe.GetWriteFD()), + spec); } //////////////////////////////////////////////////////////////////////////////// diff --git a/yt/yt/library/process/pipe.h b/yt/yt/library/process/pipe.h index 1a827ac977..b4d1b3916d 100644 --- a/yt/yt/library/process/pipe.h +++ b/yt/yt/library/process/pipe.h @@ -91,7 +91,7 @@ private: friend class TPipeFactory; }; -TString ToString(const TPipe& pipe); +void FormatValue(TStringBuilderBase* builder, const TPipe& pipe, TStringBuf spec); //////////////////////////////////////////////////////////////////////////////// |