summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--library/cpp/yt/logging/logger-inl.h2
-rw-r--r--library/cpp/yt/logging/logger.h2
-rw-r--r--library/cpp/yt/logging/static_analysis-inl.h13
-rw-r--r--library/cpp/yt/memory/ref.cpp18
-rw-r--r--library/cpp/yt/memory/ref.h10
-rw-r--r--library/cpp/yt/misc/wrapper_traits.h2
-rw-r--r--library/cpp/yt/string/format-inl.h1115
-rw-r--r--library/cpp/yt/string/format.h70
-rw-r--r--library/cpp/yt/string/format_analyser-inl.h43
-rw-r--r--library/cpp/yt/string/format_analyser.h69
-rw-r--r--library/cpp/yt/string/format_arg-inl.h67
-rw-r--r--library/cpp/yt/string/format_arg.h103
-rw-r--r--library/cpp/yt/string/format_string-inl.h56
-rw-r--r--library/cpp/yt/string/format_string.h61
-rw-r--r--library/cpp/yt/string/guid.cpp5
-rw-r--r--library/cpp/yt/string/guid.h1
-rw-r--r--library/cpp/yt/string/string_builder-inl.h54
-rw-r--r--library/cpp/yt/string/string_builder.h8
-rw-r--r--library/cpp/yt/string/unittests/enum_ut.cpp4
-rw-r--r--library/cpp/yt/string/unittests/format_ut.cpp42
-rw-r--r--library/cpp/yt/string/unittests/guid_ut.cpp2
-rw-r--r--library/cpp/yt/yson_string/convert.cpp3
-rw-r--r--library/cpp/yt/yson_string/string.cpp8
-rw-r--r--library/cpp/yt/yson_string/string.h6
-rw-r--r--yt/yt/client/chaos_client/replication_card.cpp20
-rw-r--r--yt/yt/client/chaos_client/replication_card.h4
-rw-r--r--yt/yt/client/chunk_client/chunk_replica.cpp25
-rw-r--r--yt/yt/client/chunk_client/chunk_replica.h7
-rw-r--r--yt/yt/client/chunk_client/data_statistics.cpp7
-rw-r--r--yt/yt/client/chunk_client/data_statistics.h3
-rw-r--r--yt/yt/client/chunk_client/read_limit.cpp18
-rw-r--r--yt/yt/client/chunk_client/read_limit.h6
-rw-r--r--yt/yt/client/election/public.h2
-rw-r--r--yt/yt/client/query_client/query_statistics.cpp5
-rw-r--r--yt/yt/client/query_client/query_statistics.h2
-rw-r--r--yt/yt/client/queue_client/common.cpp4
-rw-r--r--yt/yt/client/queue_client/common.h2
-rw-r--r--yt/yt/client/table_client/config.cpp12
-rw-r--r--yt/yt/client/table_client/config.h2
-rw-r--r--yt/yt/client/table_client/logical_type.cpp6
-rw-r--r--yt/yt/client/table_client/logical_type.h1
-rw-r--r--yt/yt/client/table_client/row_base.cpp6
-rw-r--r--yt/yt/client/table_client/row_base.h2
-rw-r--r--yt/yt/client/table_client/schema.cpp10
-rw-r--r--yt/yt/client/table_client/schema.h3
-rw-r--r--yt/yt/client/ypath/rich.cpp5
-rw-r--r--yt/yt/client/ypath/rich.h1
-rw-r--r--yt/yt/core/concurrency/fiber.cpp1
-rw-r--r--yt/yt/core/logging/file_log_writer.cpp2
-rw-r--r--yt/yt/core/misc/error-inl.h2
-rw-r--r--yt/yt/core/misc/error_code.cpp17
-rw-r--r--yt/yt/core/misc/error_code.h13
-rw-r--r--yt/yt/core/misc/protobuf_helpers-inl.h56
-rw-r--r--yt/yt/core/misc/protobuf_helpers.h25
-rw-r--r--yt/yt/core/misc/range_formatters-inl.h25
-rw-r--r--yt/yt/core/misc/range_formatters.h22
-rw-r--r--yt/yt/core/misc/serialize_dump.h2
-rw-r--r--yt/yt/core/misc/unittests/format_ut.cpp2
-rw-r--r--yt/yt/core/net/address.cpp6
-rw-r--r--yt/yt/core/net/address.h1
-rw-r--r--yt/yt/core/rpc/service-inl.h8
-rw-r--r--yt/yt/core/yson/token.cpp24
-rw-r--r--yt/yt/core/yson/token.h2
-rw-r--r--yt/yt/library/numeric/serialize/double_array.h20
-rw-r--r--yt/yt/library/process/pipe.cpp17
-rw-r--r--yt/yt/library/process/pipe.h2
66 files changed, 1295 insertions, 869 deletions
diff --git a/library/cpp/yt/logging/logger-inl.h b/library/cpp/yt/logging/logger-inl.h
index b20526532d5..a334a371421 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 c4a4e6adefd..5cbd6818765 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 d4ec5343bc1..13cd425d581 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 b5b636c9db0..c01094846ee 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 2177778a8f3..2dfe861e759 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 8a6d5814199..1e000ebaffb 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 4d9634039c1..069538d84bf 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), format);
+ FormatValue(builder, TStringBuf(value), spec);
+}
+
+template <size_t N>
+inline void FormatValue(TStringBuilderBase* builder, const char (&value)[N], TStringBuf spec)
+{
+ 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
+template <class TArcadiaEnum>
+ requires (std::is_enum_v<TArcadiaEnum> && !TEnumTraits<TArcadiaEnum>::IsEnum)
+void FormatValue(TStringBuilderBase* builder, TArcadiaEnum value, TStringBuf /*spec*/)
{
- return RangeEnd;
+ // 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;
+ continue;
+ }
- 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;
- }
+ // Scan format part until stop symbol.
+ auto argFormatBegin = current;
+ auto argFormatEnd = argFormatBegin;
+ bool singleQuotes = false;
+ bool doubleQuotes = false;
- // Handle end of format string.
- if (argFormatEnd != format.end()) {
- ++argFormatEnd;
+ 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
+void Format(TStringBuilderBase* builder, TRuntimeFormat fmt, TArgs&&... args)
{
- 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);
- }
- }
-};
-
-////////////////////////////////////////////////////////////////////////////////
+ // 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);
-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)...);
+ 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 8e0d5f82e50..6463135f89c 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 a548a06072a..ecc83ebccb4 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 4afe3f1ca8b..9a05e3eb8cf 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 00000000000..82a77a1249a
--- /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 00000000000..8c4f354f98e
--- /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 00000000000..5187a2ac6b0
--- /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 00000000000..8eeab7d99df
--- /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 6c133a97789..2a781d032d6 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 75edbce5dbb..de169934861 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 414667954f3..683a241c9cc 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 4ac90201770..122e5d19a0a 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 2c368e85d45..be5387151a0 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 80162747ccb..14b783516c9 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 4b5eebea16e..ed02b873917 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 51a646f0ac6..6d78ea6c9db 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 164e450b72c..ed2990a51b0 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 d9f036569f0..e310405ebcb 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 fa75f29ccb7..dcafa48f632 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 62daba4e145..9581f887f76 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 a2262c04f3f..4fe3c8335d4 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 11ff43d52c9..520c4e964ce 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 34327adbf7c..1124abd6729 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 0a8b9186279..9a9cbed0a30 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 3d106043885..e862c18a54f 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 0ce33e3b0ee..5faa08a654e 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 7b79682b48e..baf25c87727 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 fd2b30b651e..1ae041a9952 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 7f9464e5612..a759a09372e 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 22beeb98bbf..d054f2c34c8 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 93e5bc409e2..94030174109 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 32a8317feb7..1ed6989885f 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 56e4ccfa1cb..9423012fa43 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 2fec0be07e4..4a677262fa2 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 63750956f2c..b7db1a8143a 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 b2970d58d6d..16703b8fc30 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 fcbb675a397..1907e7082f4 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 d815e0d58ea..1e3637fb0b8 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 9c6e7940726..d5bb5a37aae 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 ab78d81171f..2336220f63f 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 d74f0609c6e..e1dc440db95 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 3c7b72b5f53..1b0717fa268 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 89d9b50d932..371866c55a6 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 d147a3dd9a7..79dc4108f92 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 7178d248038..f713cc9c8a5 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 3ab42c092a4..2ab243522ea 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 2dd1757eeaf..adf5a308518 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 70e163c7a7f..c20ace80177 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 00000000000..7dba39874f1
--- /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 4bfdd13a2c4..6cdfa33bf9f 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());
- }
-};
+void FormatValue(TStringBuilderBase* builder, const TRange<T>& collection, TStringBuf /*spec*/);
-// TSharedRange formatter
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 7581032bee4..6a01444638d 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 cfbce436564..d8c7834e056 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 2f05fe40dfd..3044ddeb4fb 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 37626d5d234..9e139c5af4f 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 501bf06a66c..3b60e42e72a 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 7d486d83c79..3a8ad50964b 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 c9da4c92729..66c3e91075b 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 a700b4d0ab7..9991d814310 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 6584b75b5db..e4051f6604f 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 1a827ac977d..b4d1b3916d8 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);
////////////////////////////////////////////////////////////////////////////////