// Range v3 library // // Copyright Eric Niebler 2014-present // // Use, modification and distribution is subject to the // Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // Project home: https://github.com/ericniebler/range-v3 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../simple_test.hpp" #include "../test_utils.hpp" #if defined(__cpp_lib_ranges) && __cpp_lib_ranges >= 201911 // See https://github.com/ericniebler/range-v3/issues/1480 void test_bug1480() { std::vector const first{}; std::vector const second{}; auto zip_view = ::ranges::views::zip(first, second); auto fn = [&] ([[maybe_unused]] auto && ch) { }; std::ranges::for_each(zip_view, fn); } #endif int main() { using namespace ranges; std::vector vi{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; std::vector const vs{"hello", "goodbye", "hello", "goodbye"}; // All common ranges, but one single-pass { std::stringstream str{"john paul george ringo"}; using V = std::tuple; auto rng = views::zip(vi, vs, istream(str) | views::common); using Rng = decltype(rng); CPP_assert(view_); CPP_assert(!common_range); CPP_assert(!sized_range); CPP_assert(same_as< range_value_t, std::tuple>); CPP_assert(same_as< range_reference_t, common_tuple>); CPP_assert(same_as< range_rvalue_reference_t, common_tuple>); CPP_assert(convertible_to &&, range_rvalue_reference_t>); CPP_assert(input_iterator); CPP_assert(!forward_iterator); has_cardinality(rng); auto expected = to_vector(rng); ::check_equal(expected, {V{0, "hello", "john"}, V{1, "goodbye", "paul"}, V{2, "hello", "george"}, V{3, "goodbye", "ringo"}}); } // Mixed ranges and common ranges { std::stringstream str{"john paul george ringo"}; using V = std::tuple; auto rng = views::zip(vi, vs, istream(str)); CPP_assert(view_); CPP_assert(!sized_range); CPP_assert(!common_range); CPP_assert(input_iterator); CPP_assert(!forward_iterator); has_cardinality(rng); std::vector expected; copy(rng, ranges::back_inserter(expected)); ::check_equal(expected, {V{0, "hello", "john"}, V{1, "goodbye", "paul"}, V{2, "hello", "george"}, V{3, "goodbye", "ringo"}}); } auto rnd_rng = views::zip(vi, vs); using Ref = range_reference_t; static_assert(std::is_same>::value, ""); CPP_assert(view_); CPP_assert(common_range); CPP_assert(sized_range); CPP_assert(random_access_iterator); has_cardinality(rnd_rng); auto tmp = cbegin(rnd_rng) + 3; CHECK(std::get<0>(*tmp) == 3); CHECK(std::get<1>(*tmp) == "goodbye"); CHECK((rnd_rng.end() - rnd_rng.begin()) == 4); CHECK((rnd_rng.begin() - rnd_rng.end()) == -4); CHECK(rnd_rng.size() == 4u); // zip_with { std::vector v0{"a","b","c"}; std::vector v1{"x","y","z"}; auto rng = views::zip_with(std::plus{}, v0, v1); std::vector expected; copy(rng, ranges::back_inserter(expected)); ::check_equal(expected, {"ax","by","cz"}); auto rng2 = views::zip_with([] { return 42; }); static_assert(std::is_same, int>::value, ""); } // Move from a zip view { auto v0 = to>({"a","b","c"}); auto v1 = to>({"x","y","z"}); auto rng = views::zip(v0, v1); CPP_assert(random_access_range); std::vector> expected; move(rng, ranges::back_inserter(expected)); ::check_equal(expected | views::keys, {"a","b","c"}); ::check_equal(expected | views::values, {"x","y","z"}); ::check_equal(v0, {"","",""}); ::check_equal(v1, {"","",""}); move(expected, rng.begin()); ::check_equal(expected | views::keys, {"","",""}); ::check_equal(expected | views::values, {"","",""}); ::check_equal(v0, {"a","b","c"}); ::check_equal(v1, {"x","y","z"}); std::vector res; using R = decltype(rng); auto proj = [](range_reference_t p) -> MoveOnlyString& {return p.first;}; auto rng2 = rng | views::transform(proj); move(rng2, ranges::back_inserter(res)); ::check_equal(res, {"a","b","c"}); ::check_equal(v0, {"","",""}); ::check_equal(v1, {"x","y","z"}); using R2 = decltype(rng2); CPP_assert(same_as, MoveOnlyString>); CPP_assert(same_as, MoveOnlyString &>); CPP_assert(same_as, MoveOnlyString &&>); } { auto const v = to>({"a","b","c"}); auto rng = views::zip(v, v); using Rng = decltype(rng); using I = iterator_t; CPP_assert(indirectly_readable); CPP_assert(same_as< range_value_t, std::pair>); CPP_assert(same_as< range_reference_t, common_pair>); CPP_assert(same_as< range_rvalue_reference_t, common_pair>); CPP_assert(same_as< range_common_reference_t, common_pair>); } { std::vector v{1,2,3,4}; auto moved = v | views::move; using Moved = decltype(moved); CPP_assert(same_as, int &&>); auto zipped = views::zip(moved); using Zipped = decltype(zipped); CPP_assert(same_as, common_tuple >); } // This is actually a test of the logic of view_adaptor. Since the stride view // does not redefine the current member function, the base range's iter_move // function gets picked up automatically. { auto rng0 = views::zip(vi, vs); auto rng1 = views::stride(rng0, 2); CPP_assert(same_as, range_rvalue_reference_t>); CPP_assert(same_as, range_value_t>); } // Test for noexcept iter_move { static_assert(noexcept(std::declval&>() = std::declval&&>()), ""); std::unique_ptr rg1[10], rg2[10]; auto x = views::zip(rg1, rg2); std::pair, std::unique_ptr> p = iter_move(x.begin()); auto it = x.begin(); static_assert(noexcept(iter_move(it)), ""); } // Really a test for common_iterator's iter_move, but this is a good place for it. { std::unique_ptr rg1[10], rg2[10]; auto rg3 = rg2 | views::take_while([](std::unique_ptr &){return true;}); auto x = views::zip(rg1, rg3); CPP_assert(!common_range); auto y = x | views::common; std::pair, std::unique_ptr> p = iter_move(y.begin()); auto it = x.begin(); static_assert(noexcept(iter_move(it)), ""); } // Regression test for #439. { std::vector vec{0,1,2}; auto rng = vec | views::for_each([](int i) { return ranges::yield(i); }); ranges::distance(views::zip(views::iota(0), rng) | views::common); } { int const i1[] = {0,1,2,3}; int const i2[] = {4,5,6,7}; auto rng = views::zip( debug_input_view{i1}, debug_input_view{i2} ); using P = std::pair; has_cardinality(rng); ::check_equal(rng, {P{0,4},P{1,5}, P{2,6}, P{3,7}}); } { // Test with no ranges auto rng = views::zip(); using R = decltype(rng); CPP_assert(same_as, std::tuple<>>); CPP_assert(contiguous_range); has_cardinality(rng); CHECK(ranges::begin(rng) == ranges::end(rng)); CHECK(ranges::size(rng) == 0u); } { // test dangling auto true_ = [](auto&&){ return true; }; CHECK(!::is_dangling(ranges::find_if(views::zip(vi, vs), true_))); CHECK(!::is_dangling(ranges::find_if(views::zip( vi | views::move, vs | views::common ), true_))); CHECK(::is_dangling(ranges::find_if(views::zip( vi | views::filter(true_)), true_))); } { // test zip with infinite range int const i1[] = {0,1,2,3}; auto rng = views::zip(i1, views::iota(4)); has_cardinality(rng); using P = std::pair; ::check_equal(rng, {P{0,4},P{1,5}, P{2,6}, P{3,7}}); } { // test zip with infinite ranges only auto rng = views::zip(views::iota(0), views::iota(4)); has_cardinality(rng); using P = std::pair; ::check_equal(rng | views::take(4), {P{0,4},P{1,5}, P{2,6}, P{3,7}}); } { // test unknown cardinality std::stringstream str{}; auto rng = views::zip(istream(str)); has_cardinality(rng); } return test_result(); }