From be3ae4ed5d811a5bc29fef1f41effafb1ecff41e Mon Sep 17 00:00:00 2001 From: j4niwzis Date: Wed, 29 Apr 2026 18:40:34 +1000 Subject: [PATCH] Fix compile time format for ranges, style, and std --- include/fmt/color.h | 6 +++--- include/fmt/format.h | 2 +- include/fmt/ranges.h | 34 ++++++++++++++++++++-------------- include/fmt/std.h | 15 ++++++++------- test/compile-test.cc | 16 +++++++++++++++- 5 files changed, 47 insertions(+), 26 deletions(-) diff --git a/include/fmt/color.h b/include/fmt/color.h index 153784f5..24834729 100644 --- a/include/fmt/color.h +++ b/include/fmt/color.h @@ -471,7 +471,7 @@ template inline void reset_color(buffer& buffer) { template struct styled_arg : view { const T& value; text_style style; - styled_arg(const T& v, text_style s) : value(v), style(s) {} + FMT_CONSTEXPR styled_arg(const T& v, text_style s) : value(v), style(s) {} }; template @@ -583,8 +583,8 @@ inline auto format_to(OutputIt out, text_style ts, format_string fmt, template struct formatter, Char> : formatter { template - auto format(const detail::styled_arg& arg, FormatContext& ctx) const - -> decltype(ctx.out()) { + FMT_CONSTEXPR auto format(const detail::styled_arg& arg, + FormatContext& ctx) const -> decltype(ctx.out()) { const auto& ts = arg.style; auto out = ctx.out(); diff --git a/include/fmt/format.h b/include/fmt/format.h index 256a99ab..9bf24391 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -3888,7 +3888,7 @@ template class generic_context { constexpr auto out() const -> iterator { return out_; } - void advance_to(iterator it) { + constexpr void advance_to(iterator it) { if (!detail::is_back_insert_iterator()) out_ = it; } diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index d7dbc168..bf32ece0 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -69,11 +69,12 @@ struct has_member_fn_begin_end_t().begin()), // Member function overloads. template -auto range_begin(T&& rng) -> decltype(static_cast(rng).begin()) { +FMT_CONSTEXPR auto range_begin(T&& rng) + -> decltype(static_cast(rng).begin()) { return static_cast(rng).begin(); } template -auto range_end(T&& rng) -> decltype(static_cast(rng).end()) { +FMT_CONSTEXPR auto range_end(T&& rng) -> decltype(static_cast(rng).end()) { return static_cast(rng).end(); } @@ -460,7 +461,8 @@ struct range_formatter< } template - auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) { + FMT_CONSTEXPR auto format(R&& range, FormatContext& ctx) const + -> decltype(ctx.out()) { auto out = ctx.out(); auto it = detail::range_begin(range); auto end = detail::range_end(range); @@ -516,7 +518,7 @@ struct formatter< } template - auto format(range_type& range, FormatContext& ctx) const + FMT_CONSTEXPR auto format(range_type& range, FormatContext& ctx) const -> decltype(ctx.out()) { return range_formatter_.format(range, ctx); } @@ -625,7 +627,7 @@ struct join_view : detail::view { Sentinel end; basic_string_view sep; - join_view(It b, Sentinel e, basic_string_view s) + FMT_CONSTEXPR join_view(It b, Sentinel e, basic_string_view s) : begin(std::move(b)), end(e), sep(s) {} }; @@ -652,7 +654,8 @@ struct formatter, Char> { } template - auto format(view& value, FormatContext& ctx) const -> decltype(ctx.out()) { + FMT_CONSTEXPR auto format(view& value, FormatContext& ctx) const + -> decltype(ctx.out()) { using iter = conditional_t::value, It, It&>; iter it = value.begin; @@ -675,7 +678,7 @@ template struct tuple_join_view : detail::view { const Tuple& tuple; basic_string_view sep; - tuple_join_view(const Tuple& t, basic_string_view s) + FMT_CONSTEXPR tuple_join_view(const Tuple& t, basic_string_view s) : tuple(t), sep{s} {} }; @@ -694,8 +697,9 @@ struct formatter, Char, } template - auto format(const tuple_join_view& value, - FormatContext& ctx) const -> typename FormatContext::iterator { + FMT_CONSTEXPR auto format(const tuple_join_view& value, + FormatContext& ctx) const -> + typename FormatContext::iterator { return do_format(value, ctx, std::tuple_size()); } @@ -726,15 +730,17 @@ struct formatter, Char, } template - auto do_format(const tuple_join_view&, FormatContext& ctx, - std::integral_constant) const -> + FMT_CONSTEXPR auto do_format(const tuple_join_view&, + FormatContext& ctx, + std::integral_constant) const -> typename FormatContext::iterator { return ctx.out(); } template - auto do_format(const tuple_join_view& value, FormatContext& ctx, - std::integral_constant) const -> + FMT_CONSTEXPR auto do_format(const tuple_join_view& value, + FormatContext& ctx, + std::integral_constant) const -> typename FormatContext::iterator { using std::get; auto out = @@ -813,7 +819,7 @@ auto join(It begin, Sentinel end, string_view sep) -> join_view { * // Output: 01, 02, 03 */ template ::value)> -auto join(Range&& r, string_view sep) +FMT_CONSTEXPR auto join(Range&& r, string_view sep) -> join_view { return {detail::range_begin(r), detail::range_end(r), sep}; diff --git a/include/fmt/std.h b/include/fmt/std.h index b6b98bb7..dfde1fb7 100644 --- a/include/fmt/std.h +++ b/include/fmt/std.h @@ -133,8 +133,8 @@ void write_escaped_path(basic_memory_buffer& quoted, #if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT template -auto write_escaped_alternative(OutputIt out, const T& v, FormatContext& ctx) - -> OutputIt { +FMT_CONSTEXPR auto write_escaped_alternative(OutputIt out, const T& v, + FormatContext& ctx) -> OutputIt { if constexpr (has_to_string_view::value) return write_escaped_string(out, detail::to_string_view(v)); if constexpr (std::is_same_v) return write_escaped_char(out, v); @@ -508,7 +508,7 @@ template struct formatter { } template - auto format(const std::monostate&, FormatContext& ctx) const + FMT_CONSTEXPR auto format(const std::monostate&, FormatContext& ctx) const -> decltype(ctx.out()) { return detail::write(ctx.out(), "monostate"); } @@ -524,7 +524,7 @@ struct formatter - auto format(const Variant& value, FormatContext& ctx) const + FMT_CONSTEXPR20 auto format(const Variant& value, FormatContext& ctx) const -> decltype(ctx.out()) { auto out = ctx.out(); @@ -635,7 +635,7 @@ struct formatter< } template - auto format(const std::exception& ex, Context& ctx) const + FMT_CONSTEXPR auto format(const std::exception& ex, Context& ctx) const -> decltype(ctx.out()) { auto out = ctx.out(); #if FMT_USE_RTTI @@ -690,11 +690,12 @@ struct formatter struct formatter : formatter { - static auto format_as(std::byte b) -> unsigned char { + FMT_CONSTEXPR static auto format_as(std::byte b) -> unsigned char { return static_cast(b); } template - auto format(std::byte b, Context& ctx) const -> decltype(ctx.out()) { + FMT_CONSTEXPR auto format(std::byte b, Context& ctx) const + -> decltype(ctx.out()) { return formatter::format(format_as(b), ctx); } }; diff --git a/test/compile-test.cc b/test/compile-test.cc index 9975b441..e7fe39fd 100644 --- a/test/compile-test.cc +++ b/test/compile-test.cc @@ -13,7 +13,9 @@ #include #include "fmt/chrono.h" +#include "fmt/color.h" #include "fmt/ranges.h" +#include "fmt/std.h" #include "gmock/gmock.h" #include "gtest-extra.h" @@ -87,7 +89,6 @@ TEST(compile_test, format_escape) { EXPECT_EQ("\"abc\" ", fmt::format(FMT_COMPILE("{0:<7?}"), "abc")); } - TEST(compile_test, format_specs) { EXPECT_EQ("42", fmt::format(FMT_COMPILE("{:x}"), 0x42)); EXPECT_EQ("1.2 ms ", @@ -224,6 +225,19 @@ TEST(compile_test, constexpr_formatted_size) { FMT_CONSTEXPR20 size_t str_size = fmt::formatted_size(FMT_COMPILE("{:s}"), "abc"); EXPECT_EQ(str_size, 3); + FMT_CONSTEXPR20 size_t tuple_size = fmt::formatted_size( + FMT_COMPILE("{}"), fmt::join(std::tuple(1, 2, 3), ",")); + EXPECT_EQ(tuple_size, 5); + FMT_CONSTEXPR20 size_t array_size = fmt::formatted_size( + FMT_COMPILE("{}"), fmt::join(std::array{1, 2, 3}, ",")); + EXPECT_EQ(array_size, 5); + FMT_CONSTEXPR20 size_t styled_size = fmt::formatted_size( + FMT_COMPILE("{}"), + fmt::styled(std::array{1, 2, 3}, fmt::bg(fmt::color::green))); + EXPECT_EQ(styled_size, 32); + FMT_CONSTEXPR20 size_t variant_size = fmt::formatted_size( + FMT_COMPILE("{}"), std::variant{}); + EXPECT_EQ(variant_size, 18); } TEST(compile_test, static_format) {