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>
This commit is contained in:
linsen 2026-10-08 10:31:34 +08:00
parent 134d974f81
commit fb12b23a17

View File

@ -4088,36 +4088,60 @@ template <typename Char> 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<T&&>(values)...);
return write_padded(ctx, f, specs, static_cast<T&&>(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<iterator, basic_appender<Char>>::value) {
auto buf = basic_memory_buffer<Char>();
auto buffer_ctx =
FormatContext(basic_appender<Char>(buf), ctx.args(), ctx.locale());
f.write_body(buffer_ctx, static_cast<T&&>(values)...);
return detail::write<Char>(
ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
} else if constexpr (is_back_insert_iterator<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<T&&>(values)...);
auto buf = basic_memory_buffer<Char>();
for (auto value : storage) buf.push_back(value);
return detail::write<Char>(
ctx.out(), basic_string_view<Char>(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 <typename FormatContext, typename F, typename... T,
FMT_ENABLE_IF(std::is_same<typename FormatContext::iterator,
basic_appender<Char>>::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<Char>();
auto buffer_ctx =
FormatContext(basic_appender<Char>(buf), ctx.args(), ctx.locale());
f.write_body(buffer_ctx, static_cast<T&&>(values)...);
return detail::write<Char>(
ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
}
template <typename FormatContext, typename F, typename... T,
FMT_ENABLE_IF(!std::is_same<typename FormatContext::iterator,
basic_appender<Char>>::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<T&&>(values)...);
auto buf = basic_memory_buffer<Char>();
for (auto value : storage) buf.push_back(value);
return detail::write<Char>(
ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
}
template <typename FormatContext, typename F, typename... T,
FMT_ENABLE_IF(
!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()) {
// 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