Permalink
Browse files

Allow map_pack to apply 1:n mappings

  • Loading branch information...
Naios committed Jul 11, 2017
1 parent 7c23554 commit 341025a1317d608e4c6ab7246c8b3ae01b38b84a
Showing with 326 additions and 24 deletions.
  1. +1 −0 docs/hpx.idx
  2. +209 −24 hpx/util/detail/pack_traversal_impl.hpp
  3. +12 −0 hpx/util/pack_traversal.hpp
  4. +104 −0 tests/unit/util/pack_traversal.cpp
View
@@ -536,6 +536,7 @@ invoke_fused_r "" "header\.hpx\.util\.invoke_fused_r.*"
# hpx/util/pack_traversal.hpp
map_pack "" "header\.hpx\.util\.pack_traversal.*"
spread_this "" "header\.hpx\.util\.pack_traversal.*"
traverse_pack "" "header\.hpx\.util\.pack_traversal.*"
# hpx/util/unwrapped.hpp
@@ -11,6 +11,7 @@
#include <hpx/traits/is_range.hpp>
#include <hpx/traits/is_tuple_like.hpp>
#include <hpx/util/always_void.hpp>
#include <hpx/util/detail/pack.hpp>
#include <hpx/util/invoke.hpp>
#include <hpx/util/invoke_fused.hpp>
#include <hpx/util/result_of.hpp>
@@ -25,6 +26,173 @@
namespace hpx {
namespace util {
namespace detail {
/// Exposes useful facilities for dealing with 1:n mappings
namespace spreading {
/// A struct to mark a tuple to be unpacked into the parent context
template <typename... T>
class spread_box
{
tuple<T...> boxed_;
public:
explicit spread_box(tuple<T...> boxed)
: boxed_(std::move(boxed))
{
}
tuple<T...> unbox()
{
return std::move(boxed_);
}
};
/// Deduces to a true_type if the fiven type is a flat tuple
template <typename T>
struct is_spread : std::false_type
{
};
template <typename... T>
struct is_spread<spread_box<T...>> : std::true_type
{
};
/// Converts types to the type and spread_box objects to its
/// underlying tuple.
template <typename T>
T unpack(T&& type)
{
return std::forward<T>(type);
}
template <typename... T>
auto unpack(spread_box<T...> type) -> decltype(type.unbox())
{
return type.unbox();
}
/// Deduces to the type unpack is returning when called with the
/// the given type T.
template <typename T>
using unpacked_of_t = decltype(unpack(std::declval<T>()));
/// Converts types to the type and spread_box objects to its
/// underlying tuple. If the type is mapped to zero elements,
/// the return type will be void.
template <typename T>
auto unpack_or_void(T&& type)
-> decltype(unpack(std::forward<T>(type)))
{
return unpack(std::forward<T>(type));
}
inline void unpack_or_void(spread_box<>)
{
}
/// Converts types to the a tuple carrying the single type and
/// spread_box objects to its underlying tuple.
template <typename T>
tuple<T> undecorate(T&& type)
{
return {std::forward<T>(type)};
}
template <typename... T>
auto undecorate(spread_box<T...> type) -> decltype(type.unbox())
{
return type.unbox();
}
/// A tag for mapping the arguments to a tuple
struct functional_tupelize_tag
{
};
/// Use the recursive instantiation for a variadic pack which
/// may contain spread types
template <typename C, typename... T>
auto apply_spread_impl(std::true_type, C&& callable, T&&... args)
-> decltype(invoke_fused(std::forward<C>(callable),
tuple_cat(undecorate(std::forward<T>(args))...)))
{
return invoke_fused(std::forward<C>(callable),
tuple_cat(undecorate(std::forward<T>(args))...));
}
template <typename... T>
auto apply_spread_impl(
std::true_type, functional_tupelize_tag, T&&... args)
-> decltype(tuple_cat(undecorate(std::forward<T>(args))...))
{
// Optimized version to prevent useless tuple
// unpacking and re-assembling
return tuple_cat(undecorate(std::forward<T>(args))...);
}
/// Use the linear instantiation for variadic packs which don't
/// contain spread types.
template <typename C, typename... T>
auto apply_spread_impl(std::false_type, C&& callable, T&&... args)
-> typename invoke_result<C, T...>::type
{
return hpx::util::invoke(
std::forward<C>(callable), std::forward<T>(args)...);
}
template <typename... T>
tuple<T...> apply_spread_impl(
std::false_type, functional_tupelize_tag, T&&... args)
{
// Optimized version to prevent useless tuple
// unpacking and re-assembling
return tuple<T...>{std::forward<T>(args)...};
}
/// Deduces to a true_type if any of the given types marks
/// the underlying type to be spread into the current context.
template <typename... T>
using is_any_spread_t = any_of<is_spread<T>...>;
template <typename C, typename... T>
auto map_spread(C&& callable, T&&... args)
-> decltype(apply_spread_impl(is_any_spread_t<T...>{},
std::forward<C>(callable), std::forward<T>(args)...))
{
// Check whether any of the args is a detail::flatted_tuple_t,
// if not, use the linear called version for better
// compilation speed.
return apply_spread_impl(is_any_spread_t<T...>{},
std::forward<C>(callable), std::forward<T>(args)...);
}
/// Converts the given variadic arguments into a tuple in a way
/// that spread return values are inserted into the current pack.
template <typename... T>
auto tupelize(T&&... args) -> decltype(
map_spread(functional_tupelize_tag{}, std::forward<T>(args)...))
{
return map_spread(
functional_tupelize_tag{}, std::forward<T>(args)...);
}
/// Converts an empty tuple to void
template <typename First, typename... Rest>
tuple<First, Rest...> voidify_empty_tuple(tuple<First, Rest...> val)
{
return std::move(val);
}
inline void voidify_empty_tuple(tuple<>)
{
}
/// Converts the given variadic arguments into a tuple in a way
/// that spread return values are inserted into the current pack.
///
/// If the returned tuple is empty, voidis returned instead.
template <typename... T>
auto tupelize_or_void(T&&... args) -> decltype(
voidify_empty_tuple(tupelize(std::forward<T>(args)...)))
{
return voidify_empty_tuple(tupelize(std::forward<T>(args)...));
}
} // end namespace spreading
/// Just traverses the pack with the given callable object,
/// no result is returned or preserved.
struct strategy_traverse_tag
@@ -236,7 +404,8 @@ namespace util {
/// version.
template <typename Container, typename Mapping>
using mapped_type_from_t = dereferenced_of_t<
typename invoke_result<Mapping, element_of_t<Container>>::type>;
spreading::unpacked_of_t<typename invoke_result<Mapping,
element_of_t<Container>>::type>>;
/// We create a new container, which may hold the resulting type
template <typename M, typename T>
@@ -266,8 +435,8 @@ namespace util {
for (auto&& val :
container_accessor_of(std::forward<T>(container)))
{
remapped.push_back(std::forward<M>(mapper)(
std::forward<decltype(val)>(val)));
remapped.push_back(spreading::unpack(std::forward<M>(
mapper)(std::forward<decltype(val)>(val))));
}
return remapped; // RVO
@@ -282,8 +451,8 @@ namespace util {
for (auto&& val :
container_accessor_of(std::forward<T>(container)))
{
val = std::forward<M>(mapper)(
std::forward<decltype(val)>(val));
val = spreading::unpack(std::forward<M>(mapper)(
std::forward<decltype(val)>(val)));
}
return std::forward<T>(container);
}
@@ -359,14 +528,24 @@ namespace util {
#endif
>
{
struct materializer
{
template <typename... Args>
Base<Args...> operator()(Args&&... args) const
{
return Base<Args...>{std::forward<Args>(args)...};
}
};
M mapper_;
template <typename... Args>
auto operator()(Args&&... args)
-> Base<typename invoke_result<M, OldArgs>::type...>
-> decltype(spreading::map_spread(materializer{},
std::declval<M>()(std::forward<Args>(args))...))
{
return Base<typename invoke_result<M, OldArgs>::type...>{
mapper_(std::forward<Args>(args))...};
return spreading::map_spread(
materializer{}, mapper_(std::forward<Args>(args))...);
}
};
template <typename M, template <typename...> class Base,
@@ -409,10 +588,15 @@ namespace util {
template <typename... Args>
auto operator()(Args&&... args)
-> Base<typename invoke_result<M, OldArg>::type, Size>
-> Base<decltype(spreading::unpack(std::declval<
typename invoke_result<M, OldArg>::type>())),
Size>
{
return Base<typename invoke_result<M, OldArg>::type, Size>{
{mapper_(std::forward<Args>(args))...}};
return Base<
decltype(spreading::unpack(std::declval<
typename invoke_result<M, OldArg>::type>())),
Size>{{spreading::unpack(
mapper_(std::forward<Args>(args)))...}};
}
};
template <typename M, template <typename, std::size_t> class Base,
@@ -669,13 +853,6 @@ namespace util {
std::forward<T>(element));
}
/// Boxes the given values into an according tuple
template <typename... T>
tuple<T...> box(T&&... args)
{
return tuple<T...>{std::forward<T>(args)...};
}
public:
explicit mapping_helper(M mapper)
: mapper_(std::move(mapper))
@@ -684,27 +861,35 @@ namespace util {
/// \copybrief try_traverse
template <typename T>
auto init_traverse(Strategy strategy, T&& element)
-> decltype(std::declval<mapping_helper>().try_traverse(
strategy, std::declval<T>()))
auto init_traverse(strategy_remap_tag, T&& element)
-> decltype(spreading::unpack_or_void(
std::declval<mapping_helper>().try_traverse(
strategy_remap_tag{}, std::declval<T>())))
{
return spreading::unpack_or_void(try_traverse(
strategy_remap_tag{}, std::forward<T>(element)));
}
template <typename T>
void init_traverse(strategy_traverse_tag, T&& element)
{
return try_traverse(strategy, std::forward<T>(element));
try_traverse(strategy_traverse_tag{}, std::forward<T>(element));
}
/// Calls the traversal method for every element in the pack,
/// and returns a tuple containing the remapped content.
template <typename First, typename Second, typename... T>
auto init_traverse(strategy_remap_tag strategy, First&& first,
Second&& second, T&&... rest)
-> decltype(std::declval<mapping_helper>().box(
-> decltype(spreading::tupelize_or_void(
std::declval<mapping_helper>().try_traverse(
strategy, std::forward<First>(first)),
std::declval<mapping_helper>().try_traverse(
strategy, std::forward<Second>(second)),
std::declval<mapping_helper>().try_traverse(
strategy, std::forward<T>(rest))...))
{
return box(try_traverse(strategy, std::forward<First>(first)),
return spreading::tupelize_or_void(
try_traverse(strategy, std::forward<First>(first)),
try_traverse(strategy, std::forward<Second>(second)),
try_traverse(strategy, std::forward<T>(rest))...);
}
@@ -8,6 +8,7 @@
#include <hpx/util/detail/pack_traversal_impl.hpp>
#include <type_traits>
#include <utility>
#if defined(DOXYGEN)
@@ -63,6 +64,17 @@ namespace util {
std::forward<T>(pack)...);
}
/// Indicate that the result shall be spread across the parent container
/// if possible. This can be used to create a mapper function used
/// in map_pack that maps one element to an arbitrary count (1:n).
template <typename... T>
detail::spreading::spread_box<typename std::decay<T>::type...> spread_this(
T&&... args)
{
return detail::spreading::spread_box<typename std::decay<T>::type...>(
make_tuple(std::forward<T>(args)...));
}
/// Traverses the pack with the given visitor.
///
/// This function works in the same way as `map_pack`,
Oops, something went wrong.

0 comments on commit 341025a

Please sign in to comment.