| //===------ MappedIteratorTest.cpp - Unit tests for mapped_iterator -------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/ADT/STLExtras.h" |
| #include "gtest/gtest.h" |
| |
| using namespace llvm; |
| |
| namespace { |
| |
| template <typename T> class MappedIteratorTestBasic : public testing::Test {}; |
| |
| struct Plus1Lambda { |
| auto operator()() const { |
| return [](int X) { return X + 1; }; |
| } |
| }; |
| |
| struct Plus1LambdaWithCapture { |
| const int One = 1; |
| |
| auto operator()() const { |
| return [=](int X) { return X + One; }; |
| } |
| }; |
| |
| struct Plus1FunctionRef { |
| static int plus1(int X) { return X + 1; } |
| |
| using FuncT = int (&)(int); |
| |
| FuncT operator()() const { return (FuncT)*plus1; } |
| }; |
| |
| struct Plus1FunctionPtr { |
| static int plus1(int X) { return X + 1; } |
| |
| using FuncT = int (*)(int); |
| |
| FuncT operator()() const { return plus1; } |
| }; |
| |
| struct Plus1Functor { |
| struct Plus1 { |
| int operator()(int X) const { return X + 1; } |
| }; |
| |
| auto operator()() const { return Plus1(); } |
| }; |
| |
| struct Plus1FunctorNotDefaultConstructible { |
| class PlusN { |
| const int N; |
| |
| public: |
| PlusN(int NArg) : N(NArg) {} |
| |
| int operator()(int X) const { return X + N; } |
| }; |
| |
| auto operator()() const { return PlusN(1); } |
| }; |
| |
| // clang-format off |
| using FunctionTypes = |
| ::testing::Types< |
| Plus1Lambda, |
| Plus1LambdaWithCapture, |
| Plus1FunctionRef, |
| Plus1FunctionPtr, |
| Plus1Functor, |
| Plus1FunctorNotDefaultConstructible |
| >; |
| // clang-format on |
| |
| TYPED_TEST_SUITE(MappedIteratorTestBasic, FunctionTypes, ); |
| |
| template <typename T> using GetFuncT = decltype(std::declval<T>().operator()()); |
| |
| TYPED_TEST(MappedIteratorTestBasic, DefaultConstruct) { |
| using FuncT = GetFuncT<TypeParam>; |
| using IterT = mapped_iterator<typename std::vector<int>::iterator, FuncT>; |
| TypeParam GetCallable; |
| |
| auto Func = GetCallable(); |
| (void)Func; |
| constexpr bool DefaultConstruct = |
| std::is_default_constructible_v<callable_detail::Callable<FuncT>>; |
| EXPECT_TRUE(DefaultConstruct); |
| EXPECT_TRUE(std::is_default_constructible_v<IterT>); |
| |
| if constexpr (std::is_default_constructible_v<IterT>) { |
| IterT I; |
| (void)I; |
| } |
| } |
| |
| TYPED_TEST(MappedIteratorTestBasic, CopyConstruct) { |
| std::vector<int> V({0}); |
| |
| using FuncT = GetFuncT<TypeParam>; |
| using IterT = mapped_iterator<decltype(V)::iterator, FuncT>; |
| |
| EXPECT_TRUE(std::is_copy_constructible_v<IterT>); |
| |
| if constexpr (std::is_copy_constructible_v<IterT>) { |
| TypeParam GetCallable; |
| |
| IterT I1(V.begin(), GetCallable()); |
| IterT I2(I1); |
| |
| EXPECT_EQ(I2, I1) << "copy constructed iterator is a different position"; |
| } |
| } |
| |
| TYPED_TEST(MappedIteratorTestBasic, MoveConstruct) { |
| std::vector<int> V({0}); |
| |
| using FuncT = GetFuncT<TypeParam>; |
| using IterT = mapped_iterator<decltype(V)::iterator, FuncT>; |
| |
| EXPECT_TRUE(std::is_move_constructible_v<IterT>); |
| |
| if constexpr (std::is_move_constructible_v<IterT>) { |
| TypeParam GetCallable; |
| |
| IterT I1(V.begin(), GetCallable()); |
| IterT I2(V.begin(), GetCallable()); |
| IterT I3(std::move(I2)); |
| |
| EXPECT_EQ(I3, I1) << "move constructed iterator is a different position"; |
| } |
| } |
| |
| TYPED_TEST(MappedIteratorTestBasic, CopyAssign) { |
| std::vector<int> V({0}); |
| |
| using FuncT = GetFuncT<TypeParam>; |
| using IterT = mapped_iterator<decltype(V)::iterator, FuncT>; |
| |
| EXPECT_TRUE(std::is_copy_assignable_v<IterT>); |
| |
| if constexpr (std::is_copy_assignable_v<IterT>) { |
| TypeParam GetCallable; |
| |
| IterT I1(V.begin(), GetCallable()); |
| IterT I2(V.end(), GetCallable()); |
| |
| I2 = I1; |
| |
| EXPECT_EQ(I2, I1) << "copy assigned iterator is a different position"; |
| } |
| } |
| |
| TYPED_TEST(MappedIteratorTestBasic, MoveAssign) { |
| std::vector<int> V({0}); |
| |
| using FuncT = GetFuncT<TypeParam>; |
| using IterT = mapped_iterator<decltype(V)::iterator, FuncT>; |
| |
| EXPECT_TRUE(std::is_move_assignable_v<IterT>); |
| |
| if constexpr (std::is_move_assignable_v<IterT>) { |
| TypeParam GetCallable; |
| |
| IterT I1(V.begin(), GetCallable()); |
| IterT I2(V.begin(), GetCallable()); |
| IterT I3(V.end(), GetCallable()); |
| |
| I3 = std::move(I2); |
| |
| EXPECT_EQ(I3, I1) << "move assigned iterator is a different position"; |
| } |
| } |
| |
| TYPED_TEST(MappedIteratorTestBasic, GetFunction) { |
| std::vector<int> V({0}); |
| |
| using FuncT = GetFuncT<TypeParam>; |
| using IterT = mapped_iterator<decltype(V)::iterator, FuncT>; |
| |
| TypeParam GetCallable; |
| IterT I(V.begin(), GetCallable()); |
| |
| EXPECT_EQ(I.getFunction()(200), 201); |
| } |
| |
| TYPED_TEST(MappedIteratorTestBasic, GetCurrent) { |
| std::vector<int> V({0}); |
| |
| using FuncT = GetFuncT<TypeParam>; |
| using IterT = mapped_iterator<decltype(V)::iterator, FuncT>; |
| |
| TypeParam GetCallable; |
| IterT I(V.begin(), GetCallable()); |
| |
| EXPECT_EQ(I.getCurrent(), V.begin()); |
| EXPECT_EQ(std::next(I).getCurrent(), V.end()); |
| } |
| |
| TYPED_TEST(MappedIteratorTestBasic, ApplyFunctionOnDereference) { |
| std::vector<int> V({0}); |
| TypeParam GetCallable; |
| |
| auto I = map_iterator(V.begin(), GetCallable()); |
| |
| EXPECT_EQ(*I, 1) << "should have applied function in dereference"; |
| } |
| |
| TEST(MappedIteratorTest, ApplyFunctionOnArrow) { |
| struct S { |
| int Z = 0; |
| }; |
| |
| std::vector<int> V({0}); |
| S Y; |
| S *P = &Y; |
| |
| auto I = map_iterator(V.begin(), [&](int X) -> S & { return *(P + X); }); |
| |
| I->Z = 42; |
| |
| EXPECT_EQ(Y.Z, 42) << "should have applied function during arrow"; |
| } |
| |
| TEST(MappedIteratorTest, FunctionPreservesReferences) { |
| std::vector<int> V({1}); |
| std::map<int, int> M({{1, 1}}); |
| |
| auto I = map_iterator(V.begin(), [&](int X) -> int & { return M[X]; }); |
| *I = 42; |
| |
| EXPECT_EQ(M[1], 42) << "assignment should have modified M"; |
| } |
| |
| TEST(MappedIteratorTest, CustomIteratorApplyFunctionOnDereference) { |
| struct CustomMapIterator |
| : public llvm::mapped_iterator_base<CustomMapIterator, |
| std::vector<int>::iterator, int> { |
| using BaseT::BaseT; |
| |
| /// Map the element to the iterator result type. |
| int mapElement(int X) const { return X + 1; } |
| }; |
| |
| std::vector<int> V({0}); |
| |
| CustomMapIterator I(V.begin()); |
| |
| EXPECT_EQ(*I, 1) << "should have applied function in dereference"; |
| } |
| |
| TEST(MappedIteratorTest, CustomIteratorApplyFunctionOnArrow) { |
| struct S { |
| int Z = 0; |
| }; |
| struct CustomMapIterator |
| : public llvm::mapped_iterator_base<CustomMapIterator, |
| std::vector<int>::iterator, S &> { |
| CustomMapIterator(std::vector<int>::iterator it, S *P) : BaseT(it), P(P) {} |
| |
| /// Map the element to the iterator result type. |
| S &mapElement(int X) const { return *(P + X); } |
| |
| S *P; |
| }; |
| |
| std::vector<int> V({0}); |
| S Y; |
| |
| CustomMapIterator I(V.begin(), &Y); |
| |
| I->Z = 42; |
| |
| EXPECT_EQ(Y.Z, 42) << "should have applied function during arrow"; |
| } |
| |
| TEST(MappedIteratorTest, CustomIteratorFunctionPreservesReferences) { |
| struct CustomMapIterator |
| : public llvm::mapped_iterator_base<CustomMapIterator, |
| std::vector<int>::iterator, int &> { |
| CustomMapIterator(std::vector<int>::iterator it, std::map<int, int> &M) |
| : BaseT(it), M(M) {} |
| |
| /// Map the element to the iterator result type. |
| int &mapElement(int X) const { return M[X]; } |
| |
| std::map<int, int> &M; |
| }; |
| std::vector<int> V({1}); |
| std::map<int, int> M({{1, 1}}); |
| |
| auto I = CustomMapIterator(V.begin(), M); |
| *I = 42; |
| |
| EXPECT_EQ(M[1], 42) << "assignment should have modified M"; |
| } |
| |
| } // anonymous namespace |