Skip to content

Commit

Permalink
Fixing formatting of certain kinds of ranges of ranges. (#2787)
Browse files Browse the repository at this point in the history
* Fixing formatting of certain kinds of ranges of ranges.

* Renaming const_range to range_type.
  • Loading branch information
brevzin committed Mar 5, 2022
1 parent 5c0d656 commit 0cef1f8
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 21 deletions.
46 changes: 25 additions & 21 deletions include/fmt/ranges.h
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ template <typename T, typename Char> struct is_range {
};

namespace detail {
template <typename Context, typename Element> struct range_mapper {
template <typename Context> struct range_mapper {
using mapper = arg_mapper<Context>;

template <typename T,
Expand All @@ -340,29 +340,36 @@ template <typename Context, typename Element> struct range_mapper {
};

template <typename Char, typename Element>
using range_formatter_type =
conditional_t<is_formattable<Element, Char>::value,
formatter<remove_cvref_t<decltype(
range_mapper<buffer_context<Char>, Element>{}
.map(std::declval<Element>()))>,
Char>,
fallback_formatter<Element, Char>>;
using range_formatter_type = conditional_t<
is_formattable<Element, Char>::value,
formatter<remove_cvref_t<decltype(range_mapper<buffer_context<Char>>{}.map(
std::declval<Element>()))>,
Char>,
fallback_formatter<Element, Char>>;

template <typename R>
using maybe_const_range =
conditional_t<has_const_begin_end<R>::value, const R, R>;
} // namespace detail

template <typename R, typename Char>
struct formatter<
R, Char,
enable_if_t<fmt::is_range<R, Char>::value
enable_if_t<
fmt::is_range<R, Char>::value
// Workaround a bug in MSVC 2019 and earlier.
#if !FMT_MSC_VER
&& (is_formattable<detail::uncvref_type<R>, Char>::value ||
detail::has_fallback_formatter<detail::uncvref_type<R>,
Char>::value)
&&
(is_formattable<detail::uncvref_type<detail::maybe_const_range<R>>,
Char>::value ||
detail::has_fallback_formatter<
detail::uncvref_type<detail::maybe_const_range<R>>, Char>::value)
#endif
>> {
>> {

using range_type = detail::maybe_const_range<R>;
using formatter_type =
detail::range_formatter_type<Char, detail::uncvref_type<R>>;
detail::range_formatter_type<Char, detail::uncvref_type<range_type>>;
formatter_type underlying_;
bool custom_specs_ = false;

Expand All @@ -381,20 +388,17 @@ struct formatter<
return underlying_.parse(ctx);
}

template <
typename FormatContext, typename U,
FMT_ENABLE_IF(
std::is_same<U, conditional_t<detail::has_const_begin_end<R>::value,
const R, R>>::value)>
auto format(U& range, FormatContext& ctx) const -> decltype(ctx.out()) {
template <typename FormatContext>
auto format(range_type& range, FormatContext& ctx) const
-> decltype(ctx.out()) {
#ifdef FMT_DEPRECATED_BRACED_RANGES
Char prefix = '{';
Char postfix = '}';
#else
Char prefix = detail::is_set<R>::value ? '{' : '[';
Char postfix = detail::is_set<R>::value ? '}' : ']';
#endif
detail::range_mapper<buffer_context<Char>, detail::uncvref_type<R>> mapper;
detail::range_mapper<buffer_context<Char>> mapper;
auto out = ctx.out();
*out++ = prefix;
int i = 0;
Expand Down
15 changes: 15 additions & 0 deletions test/ranges-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -364,3 +364,18 @@ TEST(ranges_test, escape_convertible_to_string_view) {
"[\"foo\"]");
}
#endif // FMT_USE_STRING_VIEW

template <typename R> struct fmt_ref_view {
R* r;

auto begin() const -> decltype(r->begin()) { return r->begin(); }
auto end() const -> decltype(r->end()) { return r->end(); }
};

TEST(ranges_test, range_of_range_of_mixed_const) {
std::vector<std::vector<int>> v = {{1, 2, 3}, {4, 5}};
EXPECT_EQ(fmt::format("{}", v), "[[1, 2, 3], [4, 5]]");

fmt_ref_view<decltype(v)> r{&v};
EXPECT_EQ(fmt::format("{}", r), "[[1, 2, 3], [4, 5]]");
}

0 comments on commit 0cef1f8

Please sign in to comment.