diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index a14b715b3dc8f..a956b9a1dc3a5 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -18,10 +18,6 @@ #include "format.h" -#if FMT_CPLUSPLUS >= 202002L -# define FMT_USE_ITERATOR_CONCEPT -#endif - FMT_BEGIN_NAMESPACE namespace detail { @@ -172,12 +168,11 @@ template class is_tuple_formattable_ { static auto all_true(...) -> std::false_type; template - static auto check(index_sequence) - -> decltype(all_true( - index_sequence{}, - integer_sequence< - bool, (is_formattable::type, - C>::value)...>{})); + static auto check(index_sequence) -> decltype(all_true( + index_sequence{}, + integer_sequence::type, + C>::value)...>{})); public: static constexpr const bool value = @@ -325,8 +320,8 @@ struct formatter - auto format(const Tuple& value, - FormatContext& ctx) const -> decltype(ctx.out()) { + auto format(const Tuple& value, FormatContext& ctx) const + -> decltype(ctx.out()) { ctx.advance_to(detail::copy(opening_bracket_, ctx.out())); detail::for_each2( formatters_, value, @@ -542,8 +537,8 @@ struct range_default_formatter< } template - auto format(range_type& range, - FormatContext& ctx) const -> decltype(ctx.out()) { + auto format(range_type& range, FormatContext& ctx) const + -> decltype(ctx.out()) { return underlying_.format(range, ctx); } }; @@ -597,29 +592,20 @@ struct formatter, Char> { return value_formatter_.parse(ctx); } -#ifdef FMT_USE_ITERATOR_CONCEPT - static constexpr bool is_input_iterator = - std::input_iterator && !std::forward_iterator; -#endif - - template -#ifdef FMT_USE_ITERATOR_CONCEPT - requires(!is_input_iterator) -#endif - auto format(const join_view& value, + template ::value)> + auto format(const join_view& value, FormatContext& ctx) const -> decltype(ctx.out()) { auto it = value.begin; return format_impl(value, ctx, it); } -#ifdef FMT_USE_ITERATOR_CONCEPT - template - requires is_input_iterator - auto format(join_view& value, - FormatContext& ctx) const -> decltype(ctx.out()) { + template ::value)> + auto format(join_view& value, FormatContext& ctx) const + -> decltype(ctx.out()) { return format_impl(value, ctx, value.begin); } -#endif template auto format_impl(const join_view& value, @@ -665,9 +651,9 @@ auto join(It begin, Sentinel end, string_view sep) -> join_view { \endrst */ template -auto join(Range&& r, - string_view sep) -> join_view { +auto join(Range&& r, string_view sep) + -> join_view { return {detail::range_begin(r), detail::range_end(r), sep}; } @@ -798,8 +784,8 @@ FMT_BEGIN_EXPORT \endrst */ template -FMT_CONSTEXPR auto join(const std::tuple& tuple, - string_view sep) -> tuple_join_view { +FMT_CONSTEXPR auto join(const std::tuple& tuple, string_view sep) + -> tuple_join_view { return {tuple, sep}; } @@ -815,8 +801,8 @@ FMT_CONSTEXPR auto join(const std::tuple& tuple, \endrst */ template -auto join(std::initializer_list list, - string_view sep) -> join_view { +auto join(std::initializer_list list, string_view sep) + -> join_view { return join(std::begin(list), std::end(list), sep); } diff --git a/test/ranges-test.cc b/test/ranges-test.cc index b47c1d7998d17..5abe5cdbedc33 100644 --- a/test/ranges-test.cc +++ b/test/ranges-test.cc @@ -167,8 +167,8 @@ struct bad_format {}; FMT_BEGIN_NAMESPACE template <> struct formatter { auto parse(format_parse_context&) -> const char* { throw bad_format(); } - auto format(not_default_formattable, - format_context& ctx) -> format_context::iterator { + auto format(not_default_formattable, format_context& ctx) + -> format_context::iterator { return ctx.out(); } }; @@ -651,3 +651,28 @@ TEST(ranges_test, input_range_join_overload) { fmt::format("{}", fmt::join(std::views::istream(iss), "."))); } #endif + +TEST(ranges_test, std_istream_iterator_join) { + std::istringstream iss("1 2 3 4 5"); + std::istream_iterator first{iss}; + std::istream_iterator last{}; + auto joined_view = fmt::join(first, last, ", "); + EXPECT_EQ("1, 2, 3, 4, 5", fmt::format("{}", std::move(joined_view))); +} + +TEST(ranges_test, movable_only_istream_iter_join) { + // Mirrors c++20 std::ranges::basic_istream_view::iterator + struct UncopyableIstreamIter : std::istream_iterator { + explicit UncopyableIstreamIter(std::istringstream& iss) + : std::istream_iterator{iss} {} + UncopyableIstreamIter(const UncopyableIstreamIter&) = delete; + UncopyableIstreamIter(UncopyableIstreamIter&&) = default; + }; + static_assert(!std::is_copy_constructible::value, ""); + + std::istringstream iss("1 2 3 4 5"); + UncopyableIstreamIter first{iss}; + std::istream_iterator last{}; + auto joined_view = fmt::join(std::move(first), last, ", "); + EXPECT_EQ("1, 2, 3, 4, 5", fmt::format("{}", std::move(joined_view))); +}