#include #include "helpers.hpp" #include #include #include #include #include "catch.hpp" using iter::imap; using Vec = const std::vector; namespace { int plusone(int i) { return i + 1; } class PlusOner { public: int operator()(int i) { return i + 1; } }; int power(int b, int e) { int acc = 1; for (int i = 0; i < e; ++i) { acc *= b; } return acc; } } TEST_CASE("imap: works with lambda, callable, and function", "[imap]") { Vec ns = {10, 20, 30}; std::vector v; SECTION("with lambda") { auto im = imap([](int i) { return i + 1; }, ns); v.assign(std::begin(im), std::end(im)); } SECTION("with callable") { SECTION("Normal call") { auto im = imap(PlusOner{}, ns); v.assign(std::begin(im), std::end(im)); } SECTION("Pipe") { auto im = ns | imap(PlusOner{}); v.assign(std::begin(im), std::end(im)); } } SECTION("with function") { auto im = imap(PlusOner{}, ns); v.assign(std::begin(im), std::end(im)); } Vec vc = {11, 21, 31}; REQUIRE(v == vc); } TEST_CASE("imap: works with pointer to member", "[imap]") { using itertest::Point; std::vector ps = {{3, 6}, {20, 25}}; std::vector v; SECTION("with pointer to member function") { auto im = imap(&Point::get_y, ps); v.assign(std::begin(im), std::end(im)); } SECTION("with pointer to data member") { auto im = imap(&Point::y, ps); v.assign(std::begin(im), std::end(im)); } Vec vc = {6, 25}; REQUIRE(v == vc); } TEST_CASE("imap: works with pointer to member function taking argument") { using itertest::Point; std::vector ps = {{10, 20}, {6, 8}, {3, 15}}; std::vector strs = {"a", "point", "pos"}; auto im = imap(&Point::prefix, ps, strs); std::vector v(std::begin(im), std::end(im)); const std::vector vc = { "a(10, 20)", "point(6, 8)", "pos(3, 15)"}; REQUIRE(v == vc); } // TODO enable once zip supports const #if 0 TEST_CASE("imap: supports const iteration", "[imap][const]") { Vec ns = {10, 20, 30}; const auto m = imap(PlusOner{}, ns); Vec v(std::begin(m), std::end(m)); Vec vc = {11, 21, 31}; REQUIRE(v == vc); } TEST_CASE("imap: const iterators can be compared to non-const iterators", "[imap][const]") { auto m = imap(PlusOner{}, Vec{}); const auto& cm = m; (void)(std::begin(m) == std::end(cm)); } #endif TEST_CASE("imap: Works with different begin and end types", "[imap]") { CharRange cr{'d'}; auto m = imap([](char c) { return std::toupper(c); }, cr); Vec v(m.begin(), m.end()); Vec vc{'A', 'B', 'C'}; REQUIRE(v == vc); } TEST_CASE("imap: works with multiple sequences", "[imap]") { Vec bases = {0, 1, 2, 3}; Vec exps = {1, 2, 3, 4}; auto im = imap(power, bases, exps); Vec v(std::begin(im), std::end(im)); Vec vc = {0, 1, 8, 81}; REQUIRE(v == vc); } TEST_CASE("imap: terminates on shortest squence", "[imap]") { Vec ns1 = {1, 2, 3, 4}; Vec ns2 = {2, 4, 6, 8, 10}; Vec vc = {3, 6, 9, 12}; SECTION("shortest sequence first") { auto im = imap([](int a, int b) { return a + b; }, ns1, ns2); Vec v(std::begin(im), std::end(im)); REQUIRE(v == vc); } SECTION("shortest sequence second") { auto im = imap([](int a, int b) { return a + b; }, ns2, ns1); Vec v(std::begin(im), std::end(im)); REQUIRE(v == vc); } } TEST_CASE("imap: operator->", "[imap]") { std::vector vs = {"ab", "abcd", "abcdefg"}; { auto m = imap([](std::string& s) { return s; }, vs); auto it = std::begin(m); REQUIRE(it->size() == 2); } { auto m = imap([](std::string& s) -> std::string& { return s; }, vs); auto it = std::begin(m); REQUIRE(it->size() == 2); } } TEST_CASE("imap: empty sequence gives nothing", "[imap]") { Vec v{}; auto im = imap(plusone, v); REQUIRE(std::begin(im) == std::end(im)); } TEST_CASE("imap: binds to lvalues, moves rvalues", "[imap]") { itertest::BasicIterable bi{1, 2}; SECTION("binds to lvalues") { imap(plusone, bi); REQUIRE_FALSE(bi.was_moved_from()); } SECTION("moves rvalues") { imap(plusone, std::move(bi)); REQUIRE(bi.was_moved_from()); } } TEST_CASE("imap: doesn't move or copy elements of iterable", "[imap]") { constexpr itertest::SolidInt arr[] = {{1}, {0}, {2}}; for (auto&& i : imap([](const itertest::SolidInt& si) { return si.getint(); }, arr)) { (void)i; } } TEST_CASE("imap: postfix ++", "[imap]") { Vec ns = {10, 20}; auto im = imap(plusone, ns); auto it = std::begin(im); it++; REQUIRE((*it) == 21); it++; REQUIRE(it == std::end(im)); } TEST_CASE("imap: iterator meets requirements", "[imap]") { std::string s{}; auto c = imap([](char) { return 1; }, s); REQUIRE(itertest::IsIterator::value); } template using ImpT = decltype(imap(std::declval(), std::declval())); TEST_CASE("imap: has correct ctor and assign ops", "[imap]") { using T1 = ImpT; auto lam = [](char) { return false; }; using T2 = ImpT; REQUIRE(itertest::IsMoveConstructibleOnly::value); REQUIRE(itertest::IsMoveConstructibleOnly::value); }