Make join() handle non-const-only begin/end ranges (#1786)

See fmtlib/fmt#1784.

Add tests that demonstrate the problem and check obvious variations.
This commit is contained in:
Tony E Lewis 2020-07-21 20:13:00 +01:00 committed by GitHub
parent d69e2da221
commit febffa4e64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 38 additions and 5 deletions

View File

@ -3420,15 +3420,14 @@ arg_join<It, Sentinel, wchar_t> join(It begin, Sentinel end, wstring_view sep) {
\endrst \endrst
*/ */
template <typename Range> template <typename Range>
arg_join<detail::iterator_t<const Range>, detail::sentinel_t<const Range>, char> arg_join<detail::iterator_t<Range>, detail::sentinel_t<Range>, char> join(
join(const Range& range, string_view sep) { Range&& range, string_view sep) {
return join(std::begin(range), std::end(range), sep); return join(std::begin(range), std::end(range), sep);
} }
template <typename Range> template <typename Range>
arg_join<detail::iterator_t<const Range>, detail::sentinel_t<const Range>, arg_join<detail::iterator_t<Range>, detail::sentinel_t<Range>, wchar_t> join(
wchar_t> Range&& range, wstring_view sep) {
join(const Range& range, wstring_view sep) {
return join(std::begin(range), std::end(range), sep); return join(std::begin(range), std::end(range), sep);
} }

View File

@ -153,3 +153,37 @@ TEST(RangesTest, JoinSentinel) {
EXPECT_EQ("{'h', 'e', 'l', 'l', 'o'}", fmt::format("{}", hello)); EXPECT_EQ("{'h', 'e', 'l', 'l', 'o'}", fmt::format("{}", hello));
EXPECT_EQ("h_e_l_l_o", fmt::format("{}", fmt::join(hello, "_"))); EXPECT_EQ("h_e_l_l_o", fmt::format("{}", fmt::join(hello, "_")));
} }
// A range that provides non-const only begin()/end() to test fmt::join handles
// that
//
// Some ranges (eg those produced by range-v3's views::filter()) can cache
// information during iteration so they only provide non-const begin()/end().
template <typename T> class non_const_only_range {
private:
std::vector<T> vec;
public:
using const_iterator = typename ::std::vector<T>::const_iterator;
template <typename... Args>
explicit non_const_only_range(Args&&... args)
: vec(::std::forward<Args>(args)...) {}
const_iterator begin() { return vec.begin(); }
const_iterator end() { return vec.end(); }
};
TEST(RangesTest, JoinRange) {
non_const_only_range<int> x(3, 0);
EXPECT_EQ("0,0,0", fmt::format("{}", fmt::join(x, ",")));
EXPECT_EQ("0,0,0",
fmt::format("{}", fmt::join(non_const_only_range<int>(3, 0), ",")));
std::vector<int> y(3, 0);
EXPECT_EQ("0,0,0", fmt::format("{}", fmt::join(y, ",")));
EXPECT_EQ("0,0,0", fmt::format("{}", fmt::join(std::vector<int>(3, 0), ",")));
const std::vector<int> z(3, 0);
EXPECT_EQ("0,0,0", fmt::format("{}", fmt::join(z, ",")));
}