From fb12b23a17c579c162ba8bf8a9a46785c7ecdd56 Mon Sep 17 00:00:00 2001 From: linsen <251731047+linsen458-spec@users.noreply.github.com> Date: Thu, 8 Oct 2026 10:31:34 +0800 Subject: [PATCH] Fix C++11 build of padded range/tuple formatting if constexpr is a C++17 feature and broke the clang-3.6 c++11 job (and -Werror builds on macos). Replace the dispatch with three write_padded overloads selected by FMT_ENABLE_IF, which works in C++11 and keeps the same behavior for appender contexts, generic back-insert iterators, and a hard error otherwise. Signed-off-by: linsen <251731047+linsen458-spec@users.noreply.github.com> --- include/fmt/format.h | 82 ++++++++++++++++++++++++++++---------------- 1 file changed, 53 insertions(+), 29 deletions(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index 54f0e41e..5dedcc4e 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -4088,36 +4088,60 @@ template class nested_format_specs { auto specs = specs_; handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_, ctx); if (specs.width == 0) return f.write_body(ctx, static_cast(values)...); + return write_padded(ctx, f, specs, static_cast(values)...); + } - // The body is formatted into a temporary buffer so that it can be padded. - // This requires a context that writes to the buffer while sharing the - // args of `ctx`, which is only possible when an output iterator of the - // context type can be pointed at storage owned here: an appender - // (context) or a back insert iterator (generic_context, used by the - // compile-time API and format_to with custom iterators). - using iterator = typename FormatContext::iterator; - if constexpr (std::is_same>::value) { - auto buf = basic_memory_buffer(); - auto buffer_ctx = - FormatContext(basic_appender(buf), ctx.args(), ctx.locale()); - f.write_body(buffer_ctx, static_cast(values)...); - return detail::write( - ctx.out(), basic_string_view(buf.data(), buf.size()), specs); - } else if constexpr (is_back_insert_iterator::value) { - using container = typename iterator::container_type; - container storage; - auto buffer_ctx = - FormatContext(iterator(storage), ctx.args(), ctx.locale()); - f.write_body(buffer_ctx, static_cast(values)...); - auto buf = basic_memory_buffer(); - for (auto value : storage) buf.push_back(value); - return detail::write( - ctx.out(), basic_string_view(buf.data(), buf.size()), specs); - } else { - // Cannot create an output iterator of the context type that writes - // into a temporary buffer. - FMT_THROW(format_error("cannot pad output with this iterator type")); - } + private: + // The body is formatted into a temporary buffer so that it can be padded. + // This requires a context that writes to the buffer while sharing the + // args of `ctx`, which is only possible when an output iterator of the + // context type can be pointed at storage owned here: an appender + // (context) or a back insert iterator (generic_context, used by the + // compile-time API and format_to with custom iterators). + template >::value)> + FMT_CONSTEXPR auto write_padded(FormatContext& ctx, const F& f, + const format_specs& specs, + T&&... values) const -> decltype(ctx.out()) { + auto buf = basic_memory_buffer(); + auto buffer_ctx = + FormatContext(basic_appender(buf), ctx.args(), ctx.locale()); + f.write_body(buffer_ctx, static_cast(values)...); + return detail::write( + ctx.out(), basic_string_view(buf.data(), buf.size()), specs); + } + + template >::value&& + is_back_insert_iterator< + typename FormatContext::iterator>::value)> + FMT_CONSTEXPR auto write_padded(FormatContext& ctx, const F& f, + const format_specs& specs, + T&&... values) const -> decltype(ctx.out()) { + using container = + typename FormatContext::iterator::container_type; + container storage; + auto buffer_ctx = + FormatContext(typename FormatContext::iterator(storage), ctx.args(), + ctx.locale()); + f.write_body(buffer_ctx, static_cast(values)...); + auto buf = basic_memory_buffer(); + for (auto value : storage) buf.push_back(value); + return detail::write( + ctx.out(), basic_string_view(buf.data(), buf.size()), specs); + } + + template ::value)> + FMT_CONSTEXPR auto write_padded(FormatContext& ctx, const F& f, + const format_specs& specs, + T&&... values) const -> decltype(ctx.out()) { + // Cannot create an output iterator of the context type that writes + // into a temporary buffer. + FMT_THROW(format_error("cannot pad output with this iterator type")); } }; } // namespace detail