/* /~` _ _ _|_. _ _ |_ | _ \_,(_)| | | || ||_|(_||_)|(/_ https://github.com/Naios/continuable v2.0.0 Copyright(c) 2015 - 2018 Denis Blank Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions : The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. **/ #include #include #include #include #include #include #include #include #include #include #include "test-continuable.hpp" using std::get; using std::make_tuple; using std::tuple; using cti::map_pack; using cti::spread_this; using cti::traverse_pack; struct all_map_float { template float operator()(T el) const { return float(el + 1.f); } }; struct my_mapper { template ::value>::type* = nullptr> float operator()(T el) const { return float(el + 1.f); } }; struct all_map { template int operator()(T) const { return 0; } }; TEST(traverse_single_test, test_container_categories) { using cti::detail::traversal::container_category_of_t; using cti::detail::traversal::container_category_tag; static_assert(std::is_same, container_category_of_t>::value, "Wrong category!"); static_assert(std::is_same, container_category_of_t>>::value, "Wrong category!"); static_assert(std::is_same, container_category_of_t>>::value, "Wrong category!"); static_assert( std::is_same, container_category_of_t>>::value, "Wrong category!"); } TEST(traverse_single_test, test_mixed_traversal) { auto res = map_pack(all_map_float{}, 0, 1.f, make_tuple(1.f, 3), std::vector>{{1, 2}, {4, 5}}, std::vector>{{1.f, 2.f}, {4.f, 5.f}}, 2); auto expected = make_tuple( // ... 1.f, 2.f, make_tuple(2.f, 4.f), std::vector>{{2.f, 3.f}, {5.f, 6.f}}, std::vector>{{2.f, 3.f}, {5.f, 6.f}}, 3.f); static_assert(std::is_same::value, "Type mismatch!"); EXPECT_TRUE((res == expected)); } TEST(traverse_single_test, test_mixed_traversal_build_regression) { // Broken build regression tests: traverse_pack(my_mapper{}, int(0), 1.f); map_pack(all_map{}, 0, std::vector{1, 2}); } TEST(traverse_single_test, test_mixed_traversal_container_container_map) { // Also a regression test auto res = map_pack(all_map{}, std::vector>{{1, 2}}); EXPECT_EQ((res[0][0]), (0)); } TEST(traverse_single_test, test_mixed_traversal_result_tuple_mapped) { auto res = map_pack( my_mapper{}, 0, 1.f, make_tuple(1.f, 3, std::vector>{{1, 2}, {4, 5}}, std::vector>{{1.f, 2.f}, {4.f, 5.f}}), 2); auto expected = make_tuple( // ... 1.f, 1.f, make_tuple(1.f, 4.f, std::vector>{{2.f, 3.f}, {5.f, 6.f}}, std::vector>{{1.f, 2.f}, {4.f, 5.f}}), 3.f); static_assert(std::is_same::value, "Type mismatch!"); EXPECT_TRUE((res == expected)); } TEST(traverse_single_test, test_mixed_traversal_all_elements_traversed) { int count = 0; traverse_pack( [&](int el) { EXPECT_EQ((el), (count + 1)); count = el; }, 1, make_tuple(2, 3, std::vector>{{4, 5}, {6, 7}})); EXPECT_EQ((count), (7)); } /* struct my_unwrapper { template ::value>::type* = nullptr> auto operator()(T future) const -> typename future_traits::result_type { return future.get(); } }; static void test_mixed_early_unwrapping() { { auto res = map_pack(my_unwrapper{}, // ... 0, 1, make_ready_future(3), make_tuple(make_ready_future(4), make_ready_future(5))); auto expected = make_tuple(0, 1, 3, make_tuple(4, 5)); static_assert(std::is_same::value, "Type mismatch!"); EXPECT_TRUE((res == expected)); } } */ template struct my_allocator { using value_type = T; using size_type = size_t; using difference_type = ptrdiff_t; using pointer = T*; using const_pointer = T const*; using reference = T&; using const_reference = T const&; unsigned state_; explicit my_allocator(unsigned state) : state_(state) { return; } template my_allocator(my_allocator const& other) : state_(other.state_) { return; } template my_allocator& operator=(my_allocator const& other) { state_ = other.state_; return *this; } template struct rebind { using other = my_allocator; }; pointer allocate(size_type n, void const* hint = nullptr) { return std::allocator{}.allocate(n, hint); } void deallocate(pointer p, size_type n) { return std::allocator{}.deallocate(p, n); } }; static void test_mixed_container_remap() { // Traits { // TODO Enable this // using detail::container_remapping::has_push_back; // EXPECT_EQ((has_push_back, int>::value), true); // EXPECT_EQ((has_push_back::value), false); } // Rebind { auto const remapper = [](unsigned short i) -> unsigned long { return i - 1; }; // Rebinds the values { std::vector source = {1, 2, 3}; std::vector dest = map_pack(remapper, source); EXPECT_TRUE((dest == decltype(dest){0, 1, 2})); } // Rebinds the allocator { static unsigned const canary = 78787; my_allocator allocator(canary); std::vector> source( allocator); // Empty { std::vector> remapped = map_pack(remapper, source); EXPECT_EQ((remapped.get_allocator().state_), (canary)); } // Non empty source.push_back(1); { std::vector> remapped = map_pack(remapper, source); EXPECT_EQ((remapped.get_allocator().state_), (canary)); } } } } struct mytester { using traversor_type = mytester; int operator()(int) { return 0; } }; struct my_int_mapper { template ::value>::type* = nullptr> float operator()(T el) const { return float(el + 1.f); } }; static void test_mixed_fall_through() { traverse_pack(my_int_mapper{}, int(0), std::vector>{make_tuple(1.f, 2.f)}, make_tuple(std::vector{1.f, 2.f})); traverse_pack(my_int_mapper{}, int(0), std::vector>{{1.f, 2.f}}, make_tuple(1.f, 2.f)); auto res1 = map_pack(my_int_mapper{}, int(0), std::vector>{{1.f, 2.f}}, make_tuple(77.f, 2)); auto res2 = map_pack( [](int) { // ... return 0; }, 1, std::vector{2, 3}); } class counter_mapper { std::reference_wrapper counter_; public: explicit counter_mapper(int& counter) : counter_(counter) { } template void operator()(T el) const { ++counter_.get(); } }; struct test_tag_1 {}; struct test_tag_2 {}; struct test_tag_3 {}; class counter_mapper_rejecting_non_tag_1 { std::reference_wrapper counter_; public: explicit counter_mapper_rejecting_non_tag_1(int& counter) : counter_(counter) { } void operator()(test_tag_1) { ++counter_.get(); } }; struct tag_shift_mapper { test_tag_2 operator()(test_tag_1) const { return {}; } test_tag_3 operator()(test_tag_2) const { return {}; } test_tag_1 operator()(test_tag_3) const { return {}; } float operator()(int) const { return 0.f; } }; class counter_mapper_rejecting_non_tag_1_sfinae { std::reference_wrapper counter_; public: explicit counter_mapper_rejecting_non_tag_1_sfinae(int& counter) : counter_(counter) { } template ::type, test_tag_1>::value>::type* = nullptr> void operator()(T) { ++counter_.get(); } }; static void test_strategic_traverse() { // Every element in the pack is visited { int counter = 0; counter_mapper mapper(counter); traverse_pack(mapper, test_tag_1{}, test_tag_2{}, test_tag_3{}); EXPECT_EQ(counter, 3); } // Every element in the pack is visited from left to right { int counter = 0; traverse_pack( [&](int el) { EXPECT_EQ(counter, el); ++counter; }, 0, 1, 2, 3); EXPECT_EQ(counter, 4); } // Elements accepted by the mapper aren't traversed: // - Signature { int counter = 0; counter_mapper_rejecting_non_tag_1 mapper(counter); traverse_pack(mapper, test_tag_1{}, test_tag_2{}, test_tag_3{}); EXPECT_EQ(counter, 1); } // - SFINAE { int counter = 0; counter_mapper_rejecting_non_tag_1_sfinae mapper(counter); traverse_pack(mapper, test_tag_1{}, test_tag_2{}, test_tag_3{}); EXPECT_EQ(counter, 1); } // Remapping works across values { tuple res = map_pack([](int i) { return i + 1; }, 0, 1, 2); auto expected = make_tuple(1, 2, 3); EXPECT_TRUE((res == expected)); } // Remapping works across types { tag_shift_mapper mapper; tuple res = map_pack(mapper, 1, test_tag_1{}, test_tag_2{}, test_tag_3{}); EXPECT_EQ(get<0>(res), 0.f); } // Remapping works with move-only objects { std::unique_ptr p1(new int(1)); std::unique_ptr p2(new int(2)); std::unique_ptr p3(new int(3)); tuple, std::unique_ptr, std::unique_ptr> res = map_pack( // Since we pass the unique_ptr's as r-value, // those should be passed as r-values to the mapper. [](std::unique_ptr&& ptr) { // We explicitly move the ownership here std::unique_ptr owned = std::move(ptr); return std::unique_ptr(new unsigned(*owned + 1)); }, std::move(p1), std::move(p2), std::move(p3)); // We expect the ownership of p1 - p3 to be invalid EXPECT_TRUE((!bool(p1))); EXPECT_TRUE((!bool(p2))); EXPECT_TRUE((!bool(p3))); EXPECT_EQ((*get<0>(res)), 2U); EXPECT_EQ((*get<1>(res)), 3U); EXPECT_EQ((*get<2>(res)), 4U); } // Move only types contained in a pack which was passed as l-value // reference is forwarded to the mapper as reference too. { std::vector> container; container.push_back(std::unique_ptr(new int(3))); std::vector res = map_pack([](std::unique_ptr& p) { return *p; }, container); EXPECT_EQ(res.size(), 1U); EXPECT_EQ(res[0], 3); } // Single object remapping returns the value itself without any boxing { int res = map_pack([](int i) { return i; }, 1); EXPECT_EQ(res, 1); } // Make it possible to pass move only objects in as reference, // while returning those as reference. { std::unique_ptr ptr(new int(7)); std::unique_ptr const& ref = map_pack( [](std::unique_ptr const& ref) -> std::unique_ptr const& { // ... return ref; }, ptr); EXPECT_EQ(*ref, 7); *ptr = 0; EXPECT_EQ(*ref, 0); } // Multiple args: Make it possible to pass move only objects in // as reference, while returning those as reference. { std::unique_ptr ptr1(new int(6)); std::unique_ptr ptr2(new int(7)); tuple const&, std::unique_ptr const&> ref = map_pack( [](std::unique_ptr const& ref) -> std::unique_ptr const& { // ... return ref; }, ptr1, ptr2); EXPECT_EQ((*get<0>(ref)), 6); EXPECT_EQ((*get<1>(ref)), 7); *ptr1 = 1; *ptr2 = 2; EXPECT_EQ((*get<0>(ref)), 1); EXPECT_EQ((*get<1>(ref)), 2); } } static void test_strategic_container_traverse() { // Every element in the container is visited // - Plain container { int counter = 0; counter_mapper mapper(counter); std::vector container; container.resize(100); traverse_pack(mapper, std::move(container)); EXPECT_EQ(counter, 100); } // - Nested container { int counter = 0; counter_mapper mapper(counter); std::vector> container; for (unsigned i = 0; i < 10; ++i) { std::vector nested; nested.resize(10); container.push_back(nested); } traverse_pack(mapper, std::move(container)); EXPECT_EQ(counter, 100); } // Every element in the container is visited from left to right { int counter = 0; traverse_pack( [&](int el) { EXPECT_EQ(counter, el); ++counter; }, std::vector{0, 1}, std::vector>{{2, 3}, {4, 5}}); EXPECT_EQ(counter, 6); } // The container type itself is changed // - Plain container { std::vector container{1, 2, 3}; std::vector res = map_pack([](int) { return 0.f; }, std::move(container)); EXPECT_EQ(res.size(), 3U); } // - Nested container { std::vector> container; std::vector> res = map_pack([](int) { return 0.f; }, std::move(container)); } // - Move only container { std::vector> container; container.push_back(std::unique_ptr(new int(5))); std::vector res = map_pack( [](std::unique_ptr&& ptr) { return *ptr; }, std::move(container)); EXPECT_EQ(res.size(), 1U); EXPECT_EQ(res[0], 5); } // Every element in the container is remapped // - Plain container { std::vector container(100, 1); auto res = map_pack([](int i) { return 2; }, std::move(container)); EXPECT_TRUE( (std::all_of(res.begin(), res.end(), [](int i) { return i == 2; }))); } // - Nested container { std::vector> container; for (unsigned i = 0; i < 10; ++i) { std::list nested(10, 1); container.push_back(nested); } auto res = map_pack([](int i) { return 2; }, std::move(container)); EXPECT_TRUE( (std::all_of(res.begin(), res.end(), [](std::list const& nested) { return std::all_of(nested.begin(), nested.end(), [](int i) { return i == 2; }); }))); } /// - Ensure correct container remapping when returning references { // l-value references { std::vector> container; container.push_back(std::unique_ptr(new int(7))); std::vector res = map_pack( [](std::unique_ptr const& ref) -> int const& { // ... return *ref; }, container); EXPECT_EQ(res.size(), 1U); EXPECT_EQ(res[0], 7); } // r-value references { std::vector>> container; container.push_back(std::unique_ptr>( new std::unique_ptr(new int(7)))); std::vector> res = map_pack( [](std::unique_ptr> & ref) -> std::unique_ptr&& { // ... return std::move(*ref); }, container); EXPECT_EQ(res.size(), 1U); EXPECT_EQ((*res[0]), 7); } } } static void test_strategic_tuple_like_traverse() { // Every element in the tuple like type is visited { int counter = 0; counter_mapper mapper(counter); traverse_pack(mapper, make_tuple(test_tag_1{}, test_tag_2{}, test_tag_3{})); EXPECT_EQ(counter, 3); } // Every element in the tuple like type is visited from left to right { int counter = 0; traverse_pack( [&](int el) { EXPECT_EQ(counter, el); ++counter; }, make_tuple(0, 1), make_tuple(make_tuple(2, 3), make_tuple(4, 5)), make_tuple(make_tuple(make_tuple(6, 7)))); EXPECT_EQ(counter, 8); } // The container tuple like type itself is changed { tag_shift_mapper mapper; tuple res = map_pack( mapper, make_tuple(1, test_tag_1{}, test_tag_2{}, test_tag_3{})); EXPECT_EQ(get<0>(res), 0.f); } // Every element in the tuple like type is remapped { tuple res = map_pack([](int) { return 1.f; }, make_tuple(0, 0, 0)); auto expected = make_tuple(1.f, 1.f, 1.f); static_assert(std::is_same::value, "Type mismatch!"); EXPECT_TRUE((res == expected)); } // Fixed size homogeneous container /* TODO Fix this test { std::array values{{1, 2, 3}}; std::array res = map_pack([](int) { return 1.f; }, values); EXPECT_TRUE((res == std::array{{1.f, 1.f, 1.f}})); }*/ // Make it possible to pass tuples containing move only objects // in as reference, while returning those as reference. { auto value = make_tuple(std::unique_ptr(new int(6)), std::unique_ptr(new int(7))); tuple const&, std::unique_ptr const&> ref = map_pack( [](std::unique_ptr const& ref) -> std::unique_ptr const& { // ... return ref; }, value); EXPECT_EQ((*get<0>(ref)), 6); EXPECT_EQ((*get<1>(ref)), 7); (*get<0>(ref)) = 1; (*get<1>(ref)) = 2; EXPECT_EQ((*get<0>(ref)), 1); EXPECT_EQ((*get<1>(ref)), 2); } } /// A mapper which duplicates the given element struct duplicate_mapper { template auto operator()(T arg) -> decltype(spread_this(arg, arg)) { return spread_this(arg, arg); } }; /// A mapper which removes the current element struct zero_mapper { template auto operator()(T arg) -> decltype(spread_this()) { return spread_this(); } }; static void test_spread_traverse() { // 1:2 mappings (multiple arguments) /* TODO Enable this { tuple res = map_pack(duplicate_mapper{}, 1, 2); auto expected = make_tuple(1, 1, 2, 2); EXPECT_TRUE((res == expected)); }*/ // 1:0 mappings { using Result = decltype(map_pack(zero_mapper{}, 0, 1, 2)); static_assert(std::is_void::value, "Failed..."); } } static void test_spread_container_traverse() { // 1:2 mappings (multiple arguments) { std::vector> res = map_pack(duplicate_mapper{}, std::vector{1}); std::vector> expected; expected.push_back(make_tuple(1, 1)); EXPECT_TRUE((res == expected)); } // 1:0 mappings { using Result = decltype(map_pack(zero_mapper{}, std::vector{1})); static_assert(std::is_void::value, "Failed..."); } } static void test_spread_tuple_like_traverse() { // 1:2 mappings (multiple arguments) { tuple> res = map_pack(duplicate_mapper{}, make_tuple(make_tuple(1, 2))); tuple> expected = make_tuple(make_tuple(1, 1, 2, 2)); EXPECT_TRUE((res == expected)); } // 1:0 mappings { using Result = decltype(map_pack(zero_mapper{}, make_tuple(make_tuple(1, 2), 1), 1)); static_assert(std::is_void::value, "Failed..."); } // 1:2 mappings (multiple arguments) { std::array res = map_pack(duplicate_mapper{}, std::array{{1, 2}}); std::array expected{{1, 1, 2, 2}}; EXPECT_TRUE((res == expected)); } // 1:0 mappings { using Result = decltype(map_pack(zero_mapper{}, std::array{{1, 2}})); static_assert(std::is_void::value, "Failed..."); } } /* TODO Convert this to gtest int main(int, char**) { test_mixed_traversal(); test_mixed_early_unwrapping(); test_mixed_container_remap(); test_mixed_fall_through(); test_strategic_traverse(); test_strategic_container_traverse(); test_strategic_tuple_like_traverse(); test_spread_traverse(); test_spread_container_traverse(); test_spread_tuple_like_traverse(); return report_errors(); } */