diff --git a/include/continuable/detail/traverse.hpp b/include/continuable/detail/traverse.hpp index e0b58d4..4e46191 100644 --- a/include/continuable/detail/traverse.hpp +++ b/include/continuable/detail/traverse.hpp @@ -697,14 +697,22 @@ class mapping_helper : protected mapping_strategy_base { /// We use the proxy function invoke_mapper here, /// because some compilers (MSVC) tend to instantiate the invocation /// before matching the tag, which leads to build failures. - template - auto match(container_category_tag, T&& element) -> decltype( - std::declval().invoke_mapper(std::forward(element))); + template + auto map(container_category_tag, T&& element) + -> decltype(std::declval().invoke_mapper( + std::forward(element))); + + /// SFINAE helper for forwarding the input to the deep remap methods in order + /// to prioritize the mapper before deep container remaps. + template + auto map(Category category, T&& element) + -> decltype(std::declval().deep_map( + category, std::forward(element))); /// SFINAE helper for elements satisfying the container /// requirements, which are not tuple like. template - auto match(container_category_tag, T&& container) + auto deep_map(container_category_tag, T&& container) -> decltype(container_remapping::remap(Strategy{}, std::forward(container), std::declval())); @@ -712,42 +720,38 @@ class mapping_helper : protected mapping_strategy_base { /// SFINAE helper for elements which are tuple like and /// that also may satisfy the container requirements template - auto match(container_category_tag, T&& tuple_like) + auto deep_map(container_category_tag, T&& tuple_like) -> decltype(tuple_like_remapping::remap(Strategy{}, std::forward(tuple_like), std::declval())); - /// This method implements the functionality for routing - /// elements through, that aren't accepted by the mapper. - /// Since the real matcher methods below are failing through SFINAE, - /// the compiler will try to specialize this function last, - /// since it's the least concrete one. - /// This works recursively, so we only call the mapper - /// with the minimal needed set of accepted arguments. - template - auto try_match(MatcherTag, T&& element) -> decltype( - std::declval().may_void(std::forward(element))) { - return this->may_void(std::forward(element)); - } - - /// Match plain elements not satisfying the tuple like or - /// container requirements. + /// Prioritize the mapper over container remapping. /// /// We use the proxy function invoke_mapper here, /// because some compilers (MSVC) tend to instantiate the invocation /// before matching the tag, which leads to build failures. - template - auto try_match(container_category_tag, T&& element) -> decltype( - std::declval().invoke_mapper(std::forward(element))) { + template + auto try_map(container_category_tag, T&& element) + -> decltype(std::declval().invoke_mapper( + std::forward(element))) { // T could be any non container or non tuple like type here, // take int or hpx::future as an example. return invoke_mapper(std::forward(element)); } + /// Forward the input to the deep remap methods in order + /// to prioritize the mapper before deep container remaps. + template + auto try_map(Category category, T&& element) + -> decltype(std::declval().try_deep_map( + category, std::forward(element))) { + return try_deep_map(category, std::forward(element)); + } + /// Match elements satisfying the container requirements, /// which are not tuple like. template - auto try_match(container_category_tag, T&& container) + auto try_deep_map(container_category_tag, T&& container) -> decltype(container_remapping::remap(Strategy{}, std::forward(container), std::declval())) { @@ -759,7 +763,7 @@ class mapping_helper : protected mapping_strategy_base { /// satisfy the container requirements /// -> We match tuple like types over container like ones template - auto try_match(container_category_tag, T&& tuple_like) + auto try_deep_map(container_category_tag, T&& tuple_like) -> decltype(tuple_like_remapping::remap(Strategy{}, std::forward(tuple_like), std::declval())) { @@ -767,27 +771,40 @@ class mapping_helper : protected mapping_strategy_base { try_traversor{this}); } + /// This method implements the functionality for routing + /// elements through, that aren't accepted by the mapper. + /// Since the real matcher methods below are failing through SFINAE, + /// the compiler will try to specialize this function last, + /// since it's the least concrete one. + /// This works recursively, so we only call the mapper + /// with the minimal needed set of accepted arguments. + template + auto try_deep_map(MatcherTag, T&& element) -> decltype( + std::declval().may_void(std::forward(element))) { + return this->may_void(std::forward(element)); + } + /// Traverses a single element. /// /// SFINAE helper: Doesn't allow routing through elements, /// that aren't accepted by the mapper template auto traverse(Strategy, T&& element) - -> decltype(std::declval().match( + -> decltype(std::declval().map( std::declval::type>>(), std::declval())); /// \copybrief traverse template auto try_traverse(Strategy, T&& element) - -> decltype(std::declval().try_match( + -> decltype(std::declval().try_map( std::declval::type>>(), std::declval())) { // We use tag dispatching here, to categorize the type T whether // it satisfies the container or tuple like requirements. // Then we can choose the underlying implementation accordingly. - return try_match(container_category_of_t::type>{}, - std::forward(element)); + return try_map(container_category_of_t::type>{}, + std::forward(element)); } public: diff --git a/test/unit-test/single/test-continuable-traverse.cpp b/test/unit-test/single/test-continuable-traverse.cpp index a6ae8f6..7117636 100644 --- a/test/unit-test/single/test-continuable-traverse.cpp +++ b/test/unit-test/single/test-continuable-traverse.cpp @@ -44,7 +44,9 @@ using cti::spread_this; using cti::traverse_pack; struct all_map_float { - template + template < + typename T, + std::enable_if_t>::value>* = nullptr> float operator()(T el) const { return float(el + 1.f); } @@ -59,8 +61,7 @@ struct my_mapper { }; struct all_map { - template - int operator()(T) const { + int operator()(int) const { return 0; } }; @@ -677,8 +678,7 @@ TEST(traverse_strategic_tuple_like_traverse, remap_references) { /// A mapper which duplicates the given element struct duplicate_mapper { - template - auto operator()(T arg) -> decltype(spread_this(arg, arg)) { + auto operator()(int arg) -> decltype(spread_this(arg, arg)) { return spread_this(arg, arg); } }; @@ -755,3 +755,16 @@ TEST(traverse_spread_tuple_like_traverse, one_to_zero_mapping_array) { using Result = decltype(map_pack(zero_mapper{}, std::array{{1, 2}})); static_assert(std::is_void::value, "Failed..."); } + +TEST(traversal_prio, prioritize_mapping) { + std::vector vec{0, 1, 2}; + int res = map_pack( + [](std::vector& vec) { + // ... + EXPECT_EQ(vec.size(), 3U); + return 4; + }, + vec); + + EXPECT_EQ(res, 4); +}