aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorarkady-e1ppa <arkady-e1ppa@yandex-team.com>2024-06-13 16:50:14 +0300
committerarkady-e1ppa <arkady-e1ppa@yandex-team.com>2024-06-13 17:13:41 +0300
commit3a8a5fac5ba669fa5bed1ccd92e75bdfc6d30d21 (patch)
tree6282648de72746e2de7193e12ecc3ee7e4ad40f5
parent3021b75cb7c16df7e4c99f77ef1509c72a79c876 (diff)
downloadydb-3a8a5fac5ba669fa5bed1ccd92e75bdfc6d30d21.tar.gz
YT-21868: Enable stat analysis in some other places
We slightly rework inner workings of stat analysis. For almost any sane use case the behavior is identical, but should compile a little faster. If you send string literals like `char[N]` or `const char*` make them constexpr. If you can't (e.g. `ex.what()` case) wrap it in `TStringBuf`, if you want the "by the book; 100% right" solution then wrap it in `TRuntimeFormat` instead. `SetRequestInfo` methods now statically checks the format. Only one error was found -- hooray! Some other internal stuff was added, a lot removed -- don't worry about it. You won't be able to see the difference 62050dfe8a9cedc1289f18e80397ff33a8e2ecdb
-rw-r--r--library/cpp/yt/logging/logger-inl.h40
-rw-r--r--library/cpp/yt/logging/logger.h15
-rw-r--r--library/cpp/yt/logging/static_analysis-inl.h146
-rw-r--r--library/cpp/yt/logging/static_analysis.h22
-rw-r--r--library/cpp/yt/logging/unittests/static_analysis_ut.cpp36
-rw-r--r--library/cpp/yt/string/format-inl.h46
-rw-r--r--library/cpp/yt/string/format.h9
-rw-r--r--library/cpp/yt/string/format_string-inl.h26
-rw-r--r--library/cpp/yt/string/format_string.cpp18
-rw-r--r--library/cpp/yt/string/format_string.h34
-rw-r--r--library/cpp/yt/string/readme.md2
-rw-r--r--library/cpp/yt/string/string_builder.h4
-rw-r--r--library/cpp/yt/string/unittests/format_ut.cpp6
-rw-r--r--library/cpp/yt/string/ya.make1
-rw-r--r--yt/yt/core/logging/log-inl.h10
-rw-r--r--yt/yt/core/rpc/service-inl.h16
-rw-r--r--yt/yt/core/rpc/service.h8
17 files changed, 133 insertions, 306 deletions
diff --git a/library/cpp/yt/logging/logger-inl.h b/library/cpp/yt/logging/logger-inl.h
index a334a37142..344770dcf6 100644
--- a/library/cpp/yt/logging/logger-inl.h
+++ b/library/cpp/yt/logging/logger-inl.h
@@ -181,26 +181,33 @@ struct TLogMessage
TStringBuf Anchor;
};
-template <size_t Length, class... TArgs>
+template <class T>
+concept CLiteralLogFormat =
+ requires (T& t) {
+ [] (const char*) { } (t);
+ };
+
+template <class... TArgs>
TLogMessage BuildLogMessage(
const TLoggingContext& loggingContext,
const TLogger& logger,
- const char (&format)[Length],
+ TFormatString<TArgs...> format,
TArgs&&... args)
{
TMessageStringBuilder builder;
- AppendLogMessageWithFormat(&builder, loggingContext, logger, format, std::forward<TArgs>(args)...);
- return {builder.Flush(), format};
+ AppendLogMessageWithFormat(&builder, loggingContext, logger, format.Get(), std::forward<TArgs>(args)...);
+ return {builder.Flush(), format.Get()};
}
-template <class T>
+template <CFormattable T>
+ requires (!CLiteralLogFormat<std::remove_cvref_t<T>>)
TLogMessage BuildLogMessage(
const TLoggingContext& loggingContext,
const TLogger& logger,
const T& obj)
{
TMessageStringBuilder builder;
- FormatValue(&builder, obj, TStringBuf());
+ FormatValue(&builder, obj, TStringBuf("v"));
if (HasMessageTags(loggingContext, logger)) {
builder.AppendString(TStringBuf(" ("));
AppendMessageTags(&builder, loggingContext, logger);
@@ -212,28 +219,23 @@ TLogMessage BuildLogMessage(
inline TLogMessage BuildLogMessage(
const TLoggingContext& loggingContext,
const TLogger& logger,
- TStringBuf message)
+ TFormatString<> format)
{
- TMessageStringBuilder builder;
- builder.AppendString(message);
- if (HasMessageTags(loggingContext, logger)) {
- builder.AppendString(TStringBuf(" ("));
- AppendMessageTags(&builder, loggingContext, logger);
- builder.AppendChar(')');
- }
- return {builder.Flush(), message};
+ return BuildLogMessage(
+ loggingContext,
+ logger,
+ format.Get());
}
-template <size_t Length>
-TLogMessage BuildLogMessage(
+inline TLogMessage BuildLogMessage(
const TLoggingContext& loggingContext,
const TLogger& logger,
- const char (&message)[Length])
+ TRuntimeFormat format)
{
return BuildLogMessage(
loggingContext,
logger,
- TStringBuf(message));
+ format.Get());
}
inline TLogMessage BuildLogMessage(
diff --git a/library/cpp/yt/logging/logger.h b/library/cpp/yt/logging/logger.h
index 100b15a6e7..4f0ed44ab7 100644
--- a/library/cpp/yt/logging/logger.h
+++ b/library/cpp/yt/logging/logger.h
@@ -22,14 +22,6 @@
#include <atomic>
-#if (!__clang__ || __clang_major__ < 16)
- #define YT_DISABLE_FORMAT_STATIC_ANALYSIS
-#endif
-
-#if !defined(NDEBUG) && !defined(YT_DISABLE_FORMAT_STATIC_ANALYSIS)
- #include "static_analysis.h"
-#endif
-
namespace NYT::NLogging {
////////////////////////////////////////////////////////////////////////////////
@@ -304,12 +296,6 @@ void LogStructuredEvent(
#define YT_LOG_EVENT(logger, level, ...) \
YT_LOG_EVENT_WITH_ANCHOR(logger, level, nullptr, __VA_ARGS__)
-#if !defined(NDEBUG) && !defined(YT_DISABLE_FORMAT_STATIC_ANALYSIS)
- #define YT_LOG_CHECK_FORMAT(...) YT_STATIC_ANALYSIS_CHECK_LOG_FORMAT(__VA_ARGS__)
-#else
- #define YT_LOG_CHECK_FORMAT(...)
-#endif
-
#define YT_LOG_EVENT_WITH_ANCHOR(logger, level, anchor, ...) \
do { \
const auto& logger__ = (logger)(); \
@@ -333,7 +319,6 @@ void LogStructuredEvent(
} \
\
auto loggingContext__ = ::NYT::NLogging::GetLoggingContext(); \
- YT_LOG_CHECK_FORMAT(__VA_ARGS__); \
auto message__ = ::NYT::NLogging::NDetail::BuildLogMessage(loggingContext__, logger__, __VA_ARGS__); \
\
if (!anchorUpToDate__) { \
diff --git a/library/cpp/yt/logging/static_analysis-inl.h b/library/cpp/yt/logging/static_analysis-inl.h
deleted file mode 100644
index 353f7be38b..0000000000
--- a/library/cpp/yt/logging/static_analysis-inl.h
+++ /dev/null
@@ -1,146 +0,0 @@
-#ifndef STATIC_ANALYSIS_INL_H_
-#error "Direct inclusion of this file is not allowed, include static_analysis.h"
-// For the sake of sane code completion.
-#include "static_analysis.h"
-#endif
-
-#include <library/cpp/yt/misc/preprocessor.h>
-
-#include <library/cpp/yt/string/format.h>
-
-#include <string_view>
-#include <variant> // monostate
-
-namespace NYT::NLogging::NDetail {
-
-////////////////////////////////////////////////////////////////////////////////
-
-// Tag for dispatching proper TFormatArg specialization.
-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
-// without invoking any ctors. With the help of macros
-// can turn non-constexpr argument pack of arguments
-// into constexpr pack of types.
-template <class... TArgs>
-struct TLoggerFormatArgs
-{ };
-
-// Used for macro conversion. Purposefully undefined.
-template <class... TArgs>
-TLoggerFormatArgs<std::remove_cvref_t<TArgs>...>
-AsFormatArgs(TArgs&&...);
-
-////////////////////////////////////////////////////////////////////////////////
-
-template <bool First, bool Second>
-struct TAnalyserDispatcher
-{
- template <class... TArgs>
- static consteval void Do(std::string_view, std::string_view, TLoggerFormatArgs<TArgs...>)
- {
- // Give up :(
- // We can't crash here, because, for example, YT_LOG_ERROR(error) exists
- // and we can't really check if error is actually TError or something else here.
- // and probably shouldn't bother trying.
- }
-};
-
-template <bool Second>
-struct TAnalyserDispatcher<true, Second>
-{
- template <class TFirst, class... TArgs>
- static consteval void Do(std::string_view str, std::string_view, TLoggerFormatArgs<TFirst, TArgs...>)
- {
- // Remove outer \"'s generated by PP_STRINGIZE.
- auto stripped = std::string_view(std::begin(str) + 1, std::size(str) - 2);
- ::NYT::NDetail::TFormatAnalyser::ValidateFormat<TLoggerFormatArg<TArgs>...>(stripped);
- }
-};
-
-template <>
-struct TAnalyserDispatcher<false, true>
-{
- template <class TFirst, class TSecond, class... TArgs>
- static consteval void Do(std::string_view, std::string_view str, TLoggerFormatArgs<TFirst, TSecond, TArgs...>)
- {
- // Remove outer \"'s generated by PP_STRINGIZE.
- auto stripped = std::string_view(std::begin(str) + 1, std::size(str) - 2);
- ::NYT::NDetail::TFormatAnalyser::ValidateFormat<TLoggerFormatArg<TArgs>...>(stripped);
- }
-};
-
-////////////////////////////////////////////////////////////////////////////////
-
-// This value is never read since homogenization works for unevaluated expressions.
-inline constexpr auto InvalidToken = std::monostate{};
-
-////////////////////////////////////////////////////////////////////////////////
-
-#define PP_VA_PICK_1_IMPL(N, ...) N
-#define PP_VA_PICK_2_IMPL(_1, N, ...) N
-
-////////////////////////////////////////////////////////////////////////////////
-
-//! Parameter pack parsing.
-
-#define YT_STATIC_ANALYSIS_CAPTURE_TYPES(...) \
- decltype(::NYT::NLogging::NDetail::AsFormatArgs(__VA_ARGS__)){}
-
-#define YT_STATIC_ANALYSIS_FIRST_TOKEN(...) \
- PP_STRINGIZE( \
- PP_VA_PICK_1_IMPL(__VA_ARGS__ __VA_OPT__(,) ::NYT::NLogging::NDetail::InvalidToken))
-
-#define YT_STATIC_ANALYSIS_SECOND_TOKEN(...) \
- PP_STRINGIZE(\
- PP_VA_PICK_2_IMPL( \
- __VA_ARGS__ __VA_OPT__(,) \
- ::NYT::NLogging::NDetail::InvalidToken, \
- ::NYT::NLogging::NDetail::InvalidToken))
-
-#define YT_STATIC_ANALYSIS_FIRST_TOKEN_COND(...) \
- YT_STATIC_ANALYSIS_FIRST_TOKEN(__VA_ARGS__)[0] == '\"'
-
-#define YT_STATIC_ANALYSIS_SECOND_TOKEN_COND(...) \
- YT_STATIC_ANALYSIS_SECOND_TOKEN(__VA_ARGS__)[0] == '\"'
-
-#undef YT_STATIC_ANALYSIS_CHECK_LOG_FORMAT
-#define YT_STATIC_ANALYSIS_CHECK_LOG_FORMAT(...) \
- ::NYT \
- ::NLogging \
- ::NDetail \
- ::TAnalyserDispatcher< \
- YT_STATIC_ANALYSIS_FIRST_TOKEN_COND(__VA_ARGS__), \
- YT_STATIC_ANALYSIS_SECOND_TOKEN_COND(__VA_ARGS__) \
- >::Do( \
- YT_STATIC_ANALYSIS_FIRST_TOKEN(__VA_ARGS__), \
- YT_STATIC_ANALYSIS_SECOND_TOKEN(__VA_ARGS__), \
- YT_STATIC_ANALYSIS_CAPTURE_TYPES(__VA_ARGS__))
-
-////////////////////////////////////////////////////////////////////////////////
-
-} // namespace NYT::NLogging::NDetail
-
-template <class T>
-struct NYT::TFormatArg<NYT::NLogging::NDetail::TLoggerFormatArg<T>>
- : public NYT::TFormatArgBase
-{
- // We mix in '\"' and ' ' which is an artifact of logging stringize.
- // We want to support YT_LOG_XXX("Value: %" PRIu64, 42)
- // for plantform independent prints of numbers.
- // String below may be converted to a token:
- // "\"Value: %\" \"u\""
- // Thus adding a \" \" sequence.
- static constexpr auto FlagSpecifiers
- = TFormatArgBase::ExtendFlags</*Hot*/ false, 2, std::array{'\"', ' '}, /*TFrom*/ T>();
-};
diff --git a/library/cpp/yt/logging/static_analysis.h b/library/cpp/yt/logging/static_analysis.h
deleted file mode 100644
index 4ccf5367d8..0000000000
--- a/library/cpp/yt/logging/static_analysis.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#pragma once
-
-namespace NYT::NLogging {
-
-////////////////////////////////////////////////////////////////////////////////
-
-// Performs a compile-time check of log arguments validity.
-// Valid argument lists are:
-// 1. (format, args...)
-// 2. (error, format, args...)
-// If format is not a string literal or argument list
-// is not valid, no check is made -- macro turns to
-// a no-op.
-#define YT_STATIC_ANALYSIS_CHECK_LOG_FORMAT(...)
-
-////////////////////////////////////////////////////////////////////////////////
-
-} // namespace NYT::NLogging
-
-#define STATIC_ANALYSIS_INL_H_
-#include "static_analysis-inl.h"
-#undef STATIC_ANALYSIS_INL_H_
diff --git a/library/cpp/yt/logging/unittests/static_analysis_ut.cpp b/library/cpp/yt/logging/unittests/static_analysis_ut.cpp
index e1bef7aaa9..7fb4c0150c 100644
--- a/library/cpp/yt/logging/unittests/static_analysis_ut.cpp
+++ b/library/cpp/yt/logging/unittests/static_analysis_ut.cpp
@@ -7,32 +7,30 @@ namespace {
////////////////////////////////////////////////////////////////////////////////
+const TLogger Logger{};
+
TEST(TStaticAnalysisTest, ValidFormats)
{
- // Mock for actual error -- we only care that
- // it is some runtime object.
- [[maybe_unused]] struct TError
- { } error;
-
- YT_LOG_CHECK_FORMAT("Hello");
- YT_LOG_CHECK_FORMAT("Hello %v", "World!");
- YT_LOG_CHECK_FORMAT("Hello %qv", "World!");
- YT_LOG_CHECK_FORMAT(error);
- YT_LOG_CHECK_FORMAT(error, "Hello");
- YT_LOG_CHECK_FORMAT(error, "Hello %Qhs", "World!");
- YT_LOG_CHECK_FORMAT("Hello %%");
- YT_LOG_CHECK_FORMAT("Hello %" PRIu64, 42);
+ YT_LOG_INFO("Hello");
+ YT_LOG_INFO("Hello %v", "World!");
+ YT_LOG_INFO("Hello %qv", "World!");
+ YT_LOG_INFO(42);
+ YT_LOG_INFO("Hello %%");
+ YT_LOG_INFO("Hello %" PRIu64, 42);
+
+ TStringBuf msg = "Hello";
+ YT_LOG_INFO(msg);
}
// Uncomment this test to see that we don't have false negatives!
// TEST(TStaticAnalysisTest, InvalidFormats)
// {
-// YT_LOG_CHECK_FORMAT("Hello", 1);
-// YT_LOG_CHECK_FORMAT("Hello %");
-// YT_LOG_CHECK_FORMAT("Hello %false");
-// YT_LOG_CHECK_FORMAT("Hello ", "World");
-// YT_LOG_CHECK_FORMAT("Hello ", "(World: %v)", 42);
-// YT_LOG_CHECK_FORMAT("Hello %lbov", 42); // There is no 'b' flag.
+// YT_LOG_INFO("Hello", 1);
+// YT_LOG_INFO("Hello %");
+// YT_LOG_INFO("Hello %false");
+// YT_LOG_INFO("Hello ", "World");
+// YT_LOG_INFO("Hello ", "(World: %v)", 42);
+// YT_LOG_INFO("Hello %lbov", 42); // There is no 'b' flag.
// }
////////////////////////////////////////////////////////////////////////////////
diff --git a/library/cpp/yt/string/format-inl.h b/library/cpp/yt/string/format-inl.h
index 8d0ef4de93..1ff2d816c5 100644
--- a/library/cpp/yt/string/format-inl.h
+++ b/library/cpp/yt/string/format-inl.h
@@ -819,41 +819,23 @@ void RunFormatter(
////////////////////////////////////////////////////////////////////////////////
template <class... TArgs>
-void Format(TStringBuilderBase* builder, TStaticFormat<TArgs...> format, TArgs&&... args)
-{
- NYT::NDetail::TValueFormatter<0, TArgs...> formatter(args...);
- NYT::NDetail::RunFormatter(builder, format.Get(), formatter);
-}
-
-template <class... TArgs>
-void Format(TStringBuilderBase* builder, TRuntimeFormat format, TArgs&&... args)
-{
- // NB(arkady-e1ppa): StaticFormat performs the
- // formattability check of the args in a way
- // that provides more useful information
- // than a simple static_assert with conjunction.
- // Additionally, the latter doesn't work properly
- // for older clang version.
- static constexpr auto argsChecker = [] {
- TStaticFormat<TArgs...>::CheckFormattability();
- return 42;
- } ();
- Y_UNUSED(argsChecker);
-
- NYT::NDetail::TValueFormatter<0, TArgs...> formatter(args...);
- NYT::NDetail::RunFormatter(builder, format.Get(), formatter);
-}
-
-template <class... TArgs>
-TString Format(TStaticFormat<TArgs...> format, TArgs&&... args)
-{
- TStringBuilder builder;
- Format(&builder, format, std::forward<TArgs>(args)...);
- return builder.Flush();
+void Format(TStringBuilderBase* builder, TFormatString<TArgs...> format, TArgs&&... args)
+{
+ // NB(arkady-e1ppa): "if constexpr" is done in order to prevent
+ // compiler from emitting "No matching function to call"
+ // when arguments are not formattable.
+ // Compiler would crash in TFormatString ctor
+ // anyway (e.g. program would not compile) but
+ // for some reason it does look ahead and emits
+ // a second error.
+ if constexpr ((CFormattable<TArgs> && ...)) {
+ NYT::NDetail::TValueFormatter<0, TArgs...> formatter(args...);
+ NYT::NDetail::RunFormatter(builder, format.Get(), formatter);
+ }
}
template <class... TArgs>
-TString Format(TRuntimeFormat format, TArgs&&... args)
+TString Format(TFormatString<TArgs...> format, TArgs&&... args)
{
TStringBuilder builder;
Format(&builder, format, std::forward<TArgs>(args)...);
diff --git a/library/cpp/yt/string/format.h b/library/cpp/yt/string/format.h
index 950b23d5f2..62b8726ddb 100644
--- a/library/cpp/yt/string/format.h
+++ b/library/cpp/yt/string/format.h
@@ -55,14 +55,9 @@ namespace NYT {
*/
template <class... TArgs>
-void Format(TStringBuilderBase* builder, TStaticFormat<TArgs...> format, TArgs&&... args);
+void Format(TStringBuilderBase* builder, TFormatString<TArgs...> format, TArgs&&... args);
template <class... TArgs>
-void Format(TStringBuilderBase* builder, TRuntimeFormat format, TArgs&&... args);
-
-template <class... TArgs>
-TString Format(TStaticFormat<TArgs...> format, TArgs&&... args);
-template <class... TArgs>
-TString Format(TRuntimeFormat format, TArgs&&... args);
+TString Format(TFormatString<TArgs...> format, TArgs&&... args);
////////////////////////////////////////////////////////////////////////////////
diff --git a/library/cpp/yt/string/format_string-inl.h b/library/cpp/yt/string/format_string-inl.h
index 5187a2ac6b..a692d9648d 100644
--- a/library/cpp/yt/string/format_string-inl.h
+++ b/library/cpp/yt/string/format_string-inl.h
@@ -11,7 +11,7 @@ namespace NYT {
template <class... TArgs>
template <class T>
requires std::constructible_from<std::string_view, T>
-consteval TBasicStaticFormat<TArgs...>::TBasicStaticFormat(const T& fmt)
+consteval TBasicFormatString<TArgs...>::TBasicFormatString(const T& fmt)
: Format_(fmt)
{
CheckFormattability();
@@ -21,13 +21,13 @@ consteval TBasicStaticFormat<TArgs...>::TBasicStaticFormat(const T& fmt)
}
template <class... TArgs>
-TStringBuf TBasicStaticFormat<TArgs...>::Get() const noexcept
+TStringBuf TBasicFormatString<TArgs...>::Get() const noexcept
{
return {Format_};
}
template <class... TArgs>
-consteval void TBasicStaticFormat<TArgs...>::CheckFormattability()
+consteval void TBasicFormatString<TArgs...>::CheckFormattability()
{
#if !defined(NDEBUG) && !defined(YT_DISABLE_FORMAT_STATIC_ANALYSIS)
using TTuple = std::tuple<std::remove_cvref_t<TArgs>...>;
@@ -42,13 +42,21 @@ consteval void TBasicStaticFormat<TArgs...>::CheckFormattability()
#endif
}
-inline TRuntimeFormat::TRuntimeFormat(TStringBuf fmt)
- : Format_(fmt)
-{ }
-
-inline TStringBuf TRuntimeFormat::Get() const noexcept
+template <class... TArgs>
+TBasicFormatString<TArgs...>::TBasicFormatString(TRuntimeFormat fmt)
+ : Format_(fmt.Get())
{
- return 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 = [] {
+ CheckFormattability();
+ return 42;
+ } ();
+ Y_UNUSED(argsChecker);
}
////////////////////////////////////////////////////////////////////////////////
diff --git a/library/cpp/yt/string/format_string.cpp b/library/cpp/yt/string/format_string.cpp
new file mode 100644
index 0000000000..31aae3ace1
--- /dev/null
+++ b/library/cpp/yt/string/format_string.cpp
@@ -0,0 +1,18 @@
+#include "format_string.h"
+
+namespace NYT {
+
+////////////////////////////////////////////////////////////////////////////////
+
+TRuntimeFormat::TRuntimeFormat(TStringBuf fmt)
+ : Format_(fmt)
+{ }
+
+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
index 8eeab7d99d..0c34763773 100644
--- a/library/cpp/yt/string/format_string.h
+++ b/library/cpp/yt/string/format_string.h
@@ -12,17 +12,32 @@ namespace NYT {
////////////////////////////////////////////////////////////////////////////////
+// Explicitly create TRuntimeFormat if you wish to
+// use runtime/non-literal value as format.
+class TRuntimeFormat
+{
+public:
+ explicit TRuntimeFormat(TStringBuf fmt);
+
+ TStringBuf Get() const noexcept;
+
+private:
+ TStringBuf Format_;
+};
+
// 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
+class TBasicFormatString
{
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);
+ consteval TBasicFormatString(const T& fmt);
+
+ TBasicFormatString(TRuntimeFormat fmt);
TStringBuf Get() const noexcept;
@@ -35,22 +50,9 @@ private:
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>...>;
+using TFormatString = TBasicFormatString<std::type_identity_t<TArgs>...>;
////////////////////////////////////////////////////////////////////////////////
diff --git a/library/cpp/yt/string/readme.md b/library/cpp/yt/string/readme.md
index eaa9e0970b..1e19eddb53 100644
--- a/library/cpp/yt/string/readme.md
+++ b/library/cpp/yt/string/readme.md
@@ -207,7 +207,7 @@ Format(fmtGood2, "World!"); // Hello, World
### Static analysis (since clang-16)
-If format string can bind to `TStaticFormat` (that is, it is a constexpr string_view or a literal) then static analysis on supplied args is performed.
+If format string can bind to `TFormatString` (that is, it is a constexpr string_view or a literal) then static analysis on supplied args is performed.
#### How to disable
diff --git a/library/cpp/yt/string/string_builder.h b/library/cpp/yt/string/string_builder.h
index 122e5d19a0..4cba45bc72 100644
--- a/library/cpp/yt/string/string_builder.h
+++ b/library/cpp/yt/string/string_builder.h
@@ -14,9 +14,7 @@ class TStringBuilder;
class TDelimitedStringBuilderWrapper;
template <class... TArgs>
-void Format(TStringBuilderBase* builder, TStaticFormat<TArgs...> fmt, TArgs&&... args);
-template <class... TArgs>
-void Format(TStringBuilderBase* builder, TRuntimeFormat fmt, TArgs&&... args);
+void Format(TStringBuilderBase* builder, TFormatString<TArgs...> fmt, TArgs&&... args);
////////////////////////////////////////////////////////////////////////////////
diff --git a/library/cpp/yt/string/unittests/format_ut.cpp b/library/cpp/yt/string/unittests/format_ut.cpp
index 14b783516c..4083338c44 100644
--- a/library/cpp/yt/string/unittests/format_ut.cpp
+++ b/library/cpp/yt/string/unittests/format_ut.cpp
@@ -217,6 +217,12 @@ TEST(TFormatTest, VectorArg)
EXPECT_EQ(FormatVector("a is %v, b is %v, c is %v", params), "a is a, b is b, c is c");
}
+TEST(TFormatTest, RuntimeFormat)
+{
+ TString format = "Hello %v";
+ EXPECT_EQ(Format(TRuntimeFormat(format), "World"), "Hello World");
+}
+
////////////////////////////////////////////////////////////////////////////////
} // namespace
diff --git a/library/cpp/yt/string/ya.make b/library/cpp/yt/string/ya.make
index 95b07ee0d1..db3a477b35 100644
--- a/library/cpp/yt/string/ya.make
+++ b/library/cpp/yt/string/ya.make
@@ -6,6 +6,7 @@ SRCS(
enum.cpp
guid.cpp
string.cpp
+ format_string.cpp
format.cpp
)
diff --git a/yt/yt/core/logging/log-inl.h b/yt/yt/core/logging/log-inl.h
index 72a7cffaed..92ba174d10 100644
--- a/yt/yt/core/logging/log-inl.h
+++ b/yt/yt/core/logging/log-inl.h
@@ -13,19 +13,19 @@ namespace NYT::NLogging {
namespace NDetail {
-template <size_t Length, class... TArgs>
+template <class... TArgs>
TLogMessage BuildLogMessage(
const TLoggingContext& loggingContext,
const TLogger& logger,
const TError& error,
- const char (&format)[Length],
+ TFormatString<TArgs...> format,
TArgs&&... args)
{
TMessageStringBuilder builder;
- AppendLogMessageWithFormat(&builder, loggingContext, logger, format, std::forward<TArgs>(args)...);
+ AppendLogMessageWithFormat(&builder, loggingContext, logger, format.Get(), std::forward<TArgs>(args)...);
builder.AppendChar('\n');
- FormatValue(&builder, error, TStringBuf());
- return {builder.Flush(), format};
+ FormatValue(&builder, error, TStringBuf("v"));
+ return {builder.Flush(), format.Get()};
}
} // namespace NDetail
diff --git a/yt/yt/core/rpc/service-inl.h b/yt/yt/core/rpc/service-inl.h
index 3b60e42e72..8647ecfd1b 100644
--- a/yt/yt/core/rpc/service-inl.h
+++ b/yt/yt/core/rpc/service-inl.h
@@ -11,38 +11,38 @@ namespace NYT::NRpc {
////////////////////////////////////////////////////////////////////////////////
template <class... TArgs>
-void IServiceContext::SetRequestInfo(const char* format, TArgs&&... args)
+void IServiceContext::SetRequestInfo(TFormatString<TArgs...> format, TArgs&&... args)
{
if (IsLoggingEnabled()) {
- SetRawRequestInfo(Format(TRuntimeFormat{format}, std::forward<TArgs>(args)...), /*incremental*/ false);
+ SetRawRequestInfo(Format(format, std::forward<TArgs>(args)...), /*incremental*/ false);
} else {
SuppressMissingRequestInfoCheck();
}
}
template <class... TArgs>
-void IServiceContext::SetIncrementalRequestInfo(const char* format, TArgs&&... args)
+void IServiceContext::SetIncrementalRequestInfo(TFormatString<TArgs...> format, TArgs&&... args)
{
if (IsLoggingEnabled()) {
- SetRawRequestInfo(Format(TRuntimeFormat{format}, std::forward<TArgs>(args)...), /*incremental*/ true);
+ SetRawRequestInfo(Format(format, std::forward<TArgs>(args)...), /*incremental*/ true);
} else {
SuppressMissingRequestInfoCheck();
}
}
template <class... TArgs>
-void IServiceContext::SetResponseInfo(const char* format, TArgs&&... args)
+void IServiceContext::SetResponseInfo(TFormatString<TArgs...> format, TArgs&&... args)
{
if (IsLoggingEnabled()) {
- SetRawResponseInfo(Format(TRuntimeFormat{format}, std::forward<TArgs>(args)...), /*incremental*/ false);
+ SetRawResponseInfo(Format(format, std::forward<TArgs>(args)...), /*incremental*/ false);
}
}
template <class... TArgs>
-void IServiceContext::SetIncrementalResponseInfo(const char* format, TArgs&&... args)
+void IServiceContext::SetIncrementalResponseInfo(TFormatString<TArgs...> format, TArgs&&... args)
{
if (IsLoggingEnabled()) {
- SetRawResponseInfo(Format(TRuntimeFormat{format}, std::forward<TArgs>(args)...), /*incremental*/ true);
+ SetRawResponseInfo(Format(format, std::forward<TArgs>(args)...), /*incremental*/ true);
}
}
diff --git a/yt/yt/core/rpc/service.h b/yt/yt/core/rpc/service.h
index b97eee9adf..cab2e99b67 100644
--- a/yt/yt/core/rpc/service.h
+++ b/yt/yt/core/rpc/service.h
@@ -250,16 +250,16 @@ struct IServiceContext
void SetResponseInfo();
template <class... TArgs>
- void SetRequestInfo(const char* format, TArgs&&... args);
+ void SetRequestInfo(TFormatString<TArgs...> format, TArgs&&... args);
template <class... TArgs>
- void SetIncrementalRequestInfo(const char* format, TArgs&&... args);
+ void SetIncrementalRequestInfo(TFormatString<TArgs...> format, TArgs&&... args);
template <class... TArgs>
- void SetResponseInfo(const char* format, TArgs&&... args);
+ void SetResponseInfo(TFormatString<TArgs...> format, TArgs&&... args);
template <class... TArgs>
- void SetIncrementalResponseInfo(const char* format, TArgs&&... args);
+ void SetIncrementalResponseInfo(TFormatString<TArgs...> format, TArgs&&... args);
//! Replies with a given message when the latter is set.
void ReplyFrom(TFuture<TSharedRefArray> asyncMessage);