Prioritize the mapper if it does accept container and tuple like elements

This commit is contained in:
Denis Blank 2018-02-10 01:37:38 +01:00
parent 20f586376f
commit 5dfe388f7f
2 changed files with 64 additions and 34 deletions

View File

@ -697,14 +697,22 @@ class mapping_helper : protected mapping_strategy_base<Strategy> {
/// We use the proxy function invoke_mapper here, /// We use the proxy function invoke_mapper here,
/// because some compilers (MSVC) tend to instantiate the invocation /// because some compilers (MSVC) tend to instantiate the invocation
/// before matching the tag, which leads to build failures. /// before matching the tag, which leads to build failures.
template <typename T> template <bool IsContainer, bool IsTupleLike, typename T>
auto match(container_category_tag<false, false>, T&& element) -> decltype( auto map(container_category_tag<IsContainer, IsTupleLike>, T&& element)
std::declval<mapping_helper>().invoke_mapper(std::forward<T>(element))); -> decltype(std::declval<mapping_helper>().invoke_mapper(
std::forward<T>(element)));
/// SFINAE helper for forwarding the input to the deep remap methods in order
/// to prioritize the mapper before deep container remaps.
template <typename Category, typename T>
auto map(Category category, T&& element)
-> decltype(std::declval<mapping_helper>().deep_map(
category, std::forward<T>(element)));
/// SFINAE helper for elements satisfying the container /// SFINAE helper for elements satisfying the container
/// requirements, which are not tuple like. /// requirements, which are not tuple like.
template <typename T> template <typename T>
auto match(container_category_tag<true, false>, T&& container) auto deep_map(container_category_tag<true, false>, T&& container)
-> decltype(container_remapping::remap(Strategy{}, -> decltype(container_remapping::remap(Strategy{},
std::forward<T>(container), std::forward<T>(container),
std::declval<traversor>())); std::declval<traversor>()));
@ -712,42 +720,38 @@ class mapping_helper : protected mapping_strategy_base<Strategy> {
/// SFINAE helper for elements which are tuple like and /// SFINAE helper for elements which are tuple like and
/// that also may satisfy the container requirements /// that also may satisfy the container requirements
template <bool IsContainer, typename T> template <bool IsContainer, typename T>
auto match(container_category_tag<IsContainer, true>, T&& tuple_like) auto deep_map(container_category_tag<IsContainer, true>, T&& tuple_like)
-> decltype(tuple_like_remapping::remap(Strategy{}, -> decltype(tuple_like_remapping::remap(Strategy{},
std::forward<T>(tuple_like), std::forward<T>(tuple_like),
std::declval<traversor>())); std::declval<traversor>()));
/// This method implements the functionality for routing /// Prioritize the mapper over container remapping.
/// 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 <typename MatcherTag, typename T>
auto try_match(MatcherTag, T&& element) -> decltype(
std::declval<mapping_helper>().may_void(std::forward<T>(element))) {
return this->may_void(std::forward<T>(element));
}
/// Match plain elements not satisfying the tuple like or
/// container requirements.
/// ///
/// We use the proxy function invoke_mapper here, /// We use the proxy function invoke_mapper here,
/// because some compilers (MSVC) tend to instantiate the invocation /// because some compilers (MSVC) tend to instantiate the invocation
/// before matching the tag, which leads to build failures. /// before matching the tag, which leads to build failures.
template <typename T> template <bool IsContainer, bool IsTupleLike, typename T>
auto try_match(container_category_tag<false, false>, T&& element) -> decltype( auto try_map(container_category_tag<IsContainer, IsTupleLike>, T&& element)
std::declval<mapping_helper>().invoke_mapper(std::forward<T>(element))) { -> decltype(std::declval<mapping_helper>().invoke_mapper(
std::forward<T>(element))) {
// T could be any non container or non tuple like type here, // T could be any non container or non tuple like type here,
// take int or hpx::future<int> as an example. // take int or hpx::future<int> as an example.
return invoke_mapper(std::forward<T>(element)); return invoke_mapper(std::forward<T>(element));
} }
/// Forward the input to the deep remap methods in order
/// to prioritize the mapper before deep container remaps.
template <typename Category, typename T>
auto try_map(Category category, T&& element)
-> decltype(std::declval<mapping_helper>().try_deep_map(
category, std::forward<T>(element))) {
return try_deep_map(category, std::forward<T>(element));
}
/// Match elements satisfying the container requirements, /// Match elements satisfying the container requirements,
/// which are not tuple like. /// which are not tuple like.
template <typename T> template <typename T>
auto try_match(container_category_tag<true, false>, T&& container) auto try_deep_map(container_category_tag<true, false>, T&& container)
-> decltype(container_remapping::remap(Strategy{}, -> decltype(container_remapping::remap(Strategy{},
std::forward<T>(container), std::forward<T>(container),
std::declval<try_traversor>())) { std::declval<try_traversor>())) {
@ -759,7 +763,7 @@ class mapping_helper : protected mapping_strategy_base<Strategy> {
/// satisfy the container requirements /// satisfy the container requirements
/// -> We match tuple like types over container like ones /// -> We match tuple like types over container like ones
template <bool IsContainer, typename T> template <bool IsContainer, typename T>
auto try_match(container_category_tag<IsContainer, true>, T&& tuple_like) auto try_deep_map(container_category_tag<IsContainer, true>, T&& tuple_like)
-> decltype(tuple_like_remapping::remap(Strategy{}, -> decltype(tuple_like_remapping::remap(Strategy{},
std::forward<T>(tuple_like), std::forward<T>(tuple_like),
std::declval<try_traversor>())) { std::declval<try_traversor>())) {
@ -767,27 +771,40 @@ class mapping_helper : protected mapping_strategy_base<Strategy> {
try_traversor{this}); 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 <typename MatcherTag, typename T>
auto try_deep_map(MatcherTag, T&& element) -> decltype(
std::declval<mapping_helper>().may_void(std::forward<T>(element))) {
return this->may_void(std::forward<T>(element));
}
/// Traverses a single element. /// Traverses a single element.
/// ///
/// SFINAE helper: Doesn't allow routing through elements, /// SFINAE helper: Doesn't allow routing through elements,
/// that aren't accepted by the mapper /// that aren't accepted by the mapper
template <typename T> template <typename T>
auto traverse(Strategy, T&& element) auto traverse(Strategy, T&& element)
-> decltype(std::declval<mapping_helper>().match( -> decltype(std::declval<mapping_helper>().map(
std::declval<container_category_of_t<typename std::decay<T>::type>>(), std::declval<container_category_of_t<typename std::decay<T>::type>>(),
std::declval<T>())); std::declval<T>()));
/// \copybrief traverse /// \copybrief traverse
template <typename T> template <typename T>
auto try_traverse(Strategy, T&& element) auto try_traverse(Strategy, T&& element)
-> decltype(std::declval<mapping_helper>().try_match( -> decltype(std::declval<mapping_helper>().try_map(
std::declval<container_category_of_t<typename std::decay<T>::type>>(), std::declval<container_category_of_t<typename std::decay<T>::type>>(),
std::declval<T>())) { std::declval<T>())) {
// We use tag dispatching here, to categorize the type T whether // We use tag dispatching here, to categorize the type T whether
// it satisfies the container or tuple like requirements. // it satisfies the container or tuple like requirements.
// Then we can choose the underlying implementation accordingly. // Then we can choose the underlying implementation accordingly.
return try_match(container_category_of_t<typename std::decay<T>::type>{}, return try_map(container_category_of_t<typename std::decay<T>::type>{},
std::forward<T>(element)); std::forward<T>(element));
} }
public: public:

View File

@ -44,7 +44,9 @@ using cti::spread_this;
using cti::traverse_pack; using cti::traverse_pack;
struct all_map_float { struct all_map_float {
template <typename T> template <
typename T,
std::enable_if_t<std::is_integral<std::decay_t<T>>::value>* = nullptr>
float operator()(T el) const { float operator()(T el) const {
return float(el + 1.f); return float(el + 1.f);
} }
@ -59,8 +61,7 @@ struct my_mapper {
}; };
struct all_map { struct all_map {
template <typename T> int operator()(int) const {
int operator()(T) const {
return 0; return 0;
} }
}; };
@ -677,8 +678,7 @@ TEST(traverse_strategic_tuple_like_traverse, remap_references) {
/// A mapper which duplicates the given element /// A mapper which duplicates the given element
struct duplicate_mapper { struct duplicate_mapper {
template <typename T> auto operator()(int arg) -> decltype(spread_this(arg, arg)) {
auto operator()(T arg) -> decltype(spread_this(arg, arg)) {
return 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<int, 2>{{1, 2}})); using Result = decltype(map_pack(zero_mapper{}, std::array<int, 2>{{1, 2}}));
static_assert(std::is_void<Result>::value, "Failed..."); static_assert(std::is_void<Result>::value, "Failed...");
} }
TEST(traversal_prio, prioritize_mapping) {
std::vector<int> vec{0, 1, 2};
int res = map_pack(
[](std::vector<int>& vec) {
// ...
EXPECT_EQ(vec.size(), 3U);
return 4;
},
vec);
EXPECT_EQ(res, 4);
}