diff options
author | arkady-e1ppa <arkady-e1ppa@yandex-team.com> | 2024-06-13 16:50:14 +0300 |
---|---|---|
committer | arkady-e1ppa <arkady-e1ppa@yandex-team.com> | 2024-06-13 17:13:41 +0300 |
commit | 3a8a5fac5ba669fa5bed1ccd92e75bdfc6d30d21 (patch) | |
tree | 6282648de72746e2de7193e12ecc3ee7e4ad40f5 /library | |
parent | 3021b75cb7c16df7e4c99f77ef1509c72a79c876 (diff) | |
download | ydb-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
Diffstat (limited to 'library')
-rw-r--r-- | library/cpp/yt/logging/logger-inl.h | 40 | ||||
-rw-r--r-- | library/cpp/yt/logging/logger.h | 15 | ||||
-rw-r--r-- | library/cpp/yt/logging/static_analysis-inl.h | 146 | ||||
-rw-r--r-- | library/cpp/yt/logging/static_analysis.h | 22 | ||||
-rw-r--r-- | library/cpp/yt/logging/unittests/static_analysis_ut.cpp | 36 | ||||
-rw-r--r-- | library/cpp/yt/string/format-inl.h | 46 | ||||
-rw-r--r-- | library/cpp/yt/string/format.h | 9 | ||||
-rw-r--r-- | library/cpp/yt/string/format_string-inl.h | 26 | ||||
-rw-r--r-- | library/cpp/yt/string/format_string.cpp | 18 | ||||
-rw-r--r-- | library/cpp/yt/string/format_string.h | 34 | ||||
-rw-r--r-- | library/cpp/yt/string/readme.md | 2 | ||||
-rw-r--r-- | library/cpp/yt/string/string_builder.h | 4 | ||||
-rw-r--r-- | library/cpp/yt/string/unittests/format_ut.cpp | 6 | ||||
-rw-r--r-- | library/cpp/yt/string/ya.make | 1 |
14 files changed, 116 insertions, 289 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 ) |