diff --git a/include/fmt/compile.h b/include/fmt/compile.h index 08d9427f..af787718 100644 --- a/include/fmt/compile.h +++ b/include/fmt/compile.h @@ -42,7 +42,7 @@ namespace detail { #endif template -auto first(const T& value, const Tail&...) -> const T& { +constexpr auto first(const T& value, const Tail&...) -> const T& { return value; } @@ -436,8 +436,8 @@ FMT_BEGIN_EXPORT template ::value)> -FMT_INLINE std::basic_string format(const CompiledFormat& cf, - const Args&... args) { +FMT_INLINE FMT_CONSTEXPR_STRING std::basic_string format( + const CompiledFormat& cf, const Args&... args) { auto s = std::basic_string(); cf.format(std::back_inserter(s), args...); return s; @@ -452,8 +452,8 @@ constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf, template ::value)> -FMT_INLINE std::basic_string format(const S&, - Args&&... args) { +FMT_INLINE FMT_CONSTEXPR_STRING std::basic_string format( + const S&, Args&&... args) { if constexpr (std::is_same::value) { constexpr auto str = basic_string_view(S()); if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') { diff --git a/include/fmt/format.h b/include/fmt/format.h index ee4c3cf4..09f2c6f4 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -117,6 +117,33 @@ # define FMT_NOINLINE #endif +// Detect constexpr std::string. +#if !FMT_USE_CONSTEVAL +# define FMT_USE_CONSTEXPR_STRING 0 +#elif defined(__cpp_lib_constexpr_string) && \ + __cpp_lib_constexpr_string >= 201907L +# if FMT_CLANG_VERSION && FMT_GLIBCXX_RELEASE +// clang + libstdc++ are able to work only starting with gcc13.3 +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113294 +# if FMT_GLIBCXX_RELEASE < 13 +# define FMT_USE_CONSTEXPR_STRING 0 +# elif FMT_GLIBCXX_RELEASE == 13 && __GLIBCXX__ < 20240521 +# define FMT_USE_CONSTEXPR_STRING 0 +# else +# define FMT_USE_CONSTEXPR_STRING 1 +# endif +# else +# define FMT_USE_CONSTEXPR_STRING 1 +# endif +#else +# define FMT_USE_CONSTEXPR_STRING 0 +#endif +#if FMT_USE_CONSTEXPR_STRING +# define FMT_CONSTEXPR_STRING constexpr +#else +# define FMT_CONSTEXPR_STRING +#endif + // GCC 4.9 doesn't support qualified names in specializations. namespace std { template struct iterator_traits> { @@ -4252,7 +4279,7 @@ FMT_NODISCARD FMT_INLINE auto format(format_string fmt, T&&... args) * std::string answer = fmt::to_string(42); */ template ::value)> -FMT_NODISCARD auto to_string(T value) -> std::string { +FMT_NODISCARD FMT_CONSTEXPR_STRING auto to_string(T value) -> std::string { // The buffer should be large enough to store the number including the sign // or "false" for bool. char buffer[max_of(detail::digits10() + 2, 5)]; @@ -4260,13 +4287,15 @@ FMT_NODISCARD auto to_string(T value) -> std::string { } template ::value)> -FMT_NODISCARD auto to_string(const T& value) -> std::string { +FMT_NODISCARD FMT_CONSTEXPR_STRING auto to_string(const T& value) + -> std::string { return to_string(format_as(value)); } template ::value && !detail::use_format_as::value)> -FMT_NODISCARD auto to_string(const T& value) -> std::string { +FMT_NODISCARD FMT_CONSTEXPR_STRING auto to_string(const T& value) + -> std::string { auto buffer = memory_buffer(); detail::write(appender(buffer), value); return {buffer.data(), buffer.size()}; diff --git a/test/compile-test.cc b/test/compile-test.cc index b4c7e4b8..b1c9052f 100644 --- a/test/compile-test.cc +++ b/test/compile-test.cc @@ -421,3 +421,49 @@ TEST(compile_time_formatting_test, multibyte_fill) { EXPECT_EQ("жж42", test_format<8>(FMT_COMPILE("{:ж>4}"), 42)); } #endif + +#if FMT_USE_CONSTEXPR_STRING + +TEST(compile_test, constexpr_format) { + { + constexpr auto result = []() { + return fmt::format(FMT_COMPILE("{}"), 1) == "1"; + }(); + EXPECT_TRUE(result); + } + + { + constexpr auto result = []() { + return fmt::format(FMT_COMPILE("{:#b}"), 42) == "0b101010"; + }(); + EXPECT_TRUE(result); + } + + { + constexpr auto result = []() { + return "**-42" == fmt::format(FMT_COMPILE("{:*>5}"), -42); + }(); + EXPECT_TRUE(result); + } + + { + constexpr auto result = []() { + return "10 " == fmt::format(FMT_COMPILE("{: ^3}"), 10); + }(); + EXPECT_TRUE(result); + } + + { + constexpr auto result = []() { + return "42 is 42" == fmt::format(FMT_COMPILE("{} is {}"), 42, 42); + }(); + EXPECT_TRUE(result); + } + + constexpr auto result = []() { + return "This is a very huge int: 1234567890" == fmt::format(FMT_COMPILE("This is a very huge int: {}"), 1234567890); + }(); + EXPECT_TRUE(result); +} + +#endif // FMT_USE_CONSTEXPR_STRING