diff --git a/include/fmt/compile.h b/include/fmt/compile.h index 77d9c907..3ffa8152 100644 --- a/include/fmt/compile.h +++ b/include/fmt/compile.h @@ -525,6 +525,40 @@ void print(const S& fmt, T&&... args) { print(stdout, fmt, std::forward(args)...); } +template class static_format_result { + private: + char data[N]; + + public: + template ::value)> + explicit FMT_CONSTEXPR static_format_result(const S& fmt, T&&... args) { + *fmt::format_to(data, fmt, std::forward(args)...) = '\0'; + } + + auto str() const -> fmt::string_view { return {data, N - 1}; } + auto c_str() const -> const char* { return data; } +}; + +/** + * Formats arguments according to the format string `fmt_str` and produces + * a string of the exact required size at compile time. Both the format string + * and the arguments must be compile-time expressions. + * + * The resulting string can be accessed as a C string via `c_str()` or as + * a `fmt::string_view` via `str()`. + * + * **Example**: + * + * // Produces the static string "42" at compile time. + * static constexpr auto result = FMT_STATIC_FORMAT("{}", 42); + * const char* s = result.c_str(); + */ +#define FMT_STATIC_FORMAT(fmt_str, ...) \ + fmt::static_format_result< \ + fmt::formatted_size(FMT_COMPILE(fmt_str), __VA_ARGS__) + 1>( \ + FMT_COMPILE(fmt_str), __VA_ARGS__) + #if FMT_USE_NONTYPE_TEMPLATE_ARGS inline namespace literals { template constexpr auto operator""_cf() { diff --git a/test/compile-test.cc b/test/compile-test.cc index 27e39336..e0a9bc9b 100644 --- a/test/compile-test.cc +++ b/test/compile-test.cc @@ -236,6 +236,12 @@ TEST(compile_test, constexpr_formatted_size) { fmt::formatted_size(FMT_COMPILE("{:s}"), "abc"); EXPECT_EQ(str_size, 3); } + +TEST(compile_test, static_format) { + constexpr auto result = FMT_STATIC_FORMAT("{}", 42); + EXPECT_STREQ(result.c_str(), "42"); + EXPECT_EQ(result.str(), "42"); +} # endif TEST(compile_test, text_and_arg) { @@ -423,22 +429,16 @@ TEST(compile_time_formatting_test, multibyte_fill) { #endif #if FMT_USE_CONSTEXPR_STRING +TEST(compile_test, constexpr_string_format) { + constexpr auto result = []() { + return fmt::format(FMT_COMPILE("{}"), 42) == "42"; + }(); + EXPECT_TRUE(result); -TEST(compile_test, constexpr_format) { - { - constexpr auto result = []() { - return fmt::format(FMT_COMPILE("{}"), 42) == "42"; - }(); - EXPECT_TRUE(result); - } - - { - // Test with a larger string to avoid small string optimization. - constexpr auto result = []() { - return fmt::format(FMT_COMPILE("{:100}"), ' ') == std::string(100, ' '); - }(); - EXPECT_TRUE(result); - } + // Test with a larger string to avoid small string optimization. + constexpr auto big = []() { + return fmt::format(FMT_COMPILE("{:100}"), ' ') == std::string(100, ' '); + }(); + EXPECT_TRUE(big); } - #endif // FMT_USE_CONSTEXPR_STRING