515 lines
18 KiB
C++
515 lines
18 KiB
C++
|
/*
|
||
|
@file
|
||
|
Defines experimental views.
|
||
|
|
||
|
@copyright Louis Dionne 2013-2016
|
||
|
Distributed under the Boost Software License, Version 1.0.
|
||
|
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
|
||
|
*/
|
||
|
|
||
|
#ifndef BOOST_HANA_EXPERIMENTAL_VIEW_HPP
|
||
|
#define BOOST_HANA_EXPERIMENTAL_VIEW_HPP
|
||
|
|
||
|
#include <boost/hana/and.hpp>
|
||
|
#include <boost/hana/at.hpp>
|
||
|
#include <boost/hana/bool.hpp>
|
||
|
#include <boost/hana/detail/decay.hpp>
|
||
|
#include <boost/hana/fold_left.hpp>
|
||
|
#include <boost/hana/functional/compose.hpp>
|
||
|
#include <boost/hana/fwd/ap.hpp>
|
||
|
#include <boost/hana/fwd/concat.hpp>
|
||
|
#include <boost/hana/fwd/drop_front.hpp>
|
||
|
#include <boost/hana/fwd/empty.hpp>
|
||
|
#include <boost/hana/fwd/equal.hpp>
|
||
|
#include <boost/hana/fwd/flatten.hpp>
|
||
|
#include <boost/hana/fwd/is_empty.hpp>
|
||
|
#include <boost/hana/fwd/less.hpp>
|
||
|
#include <boost/hana/fwd/lift.hpp>
|
||
|
#include <boost/hana/fwd/transform.hpp>
|
||
|
#include <boost/hana/integral_constant.hpp>
|
||
|
#include <boost/hana/length.hpp>
|
||
|
#include <boost/hana/lexicographical_compare.hpp>
|
||
|
#include <boost/hana/range.hpp>
|
||
|
#include <boost/hana/tuple.hpp>
|
||
|
#include <boost/hana/unpack.hpp>
|
||
|
|
||
|
#include <cstddef>
|
||
|
#include <type_traits>
|
||
|
#include <utility>
|
||
|
|
||
|
|
||
|
// Pros of views
|
||
|
// - No temporary container created between algorithms
|
||
|
// - Lazy, so only the minimum is required
|
||
|
//
|
||
|
// Cons of views
|
||
|
// - Reference semantics mean possibility for dangling references
|
||
|
// - Lose the ability to move from temporary containers
|
||
|
// - When fetching the members of a view multiple times, no caching is done.
|
||
|
// So for example, `t = transform(xs, f); at_c<0>(t); at_c<0>(t)` will
|
||
|
// compute `f(at_c<0>(xs))` twice.
|
||
|
// - push_back creates a joint_view and a single_view. The single_view holds
|
||
|
// the value as a member. When doing multiple push_backs, we end up with a
|
||
|
// joint_view<xxx, joint_view<single_view<T>, joint_view<single_view<T>, ....>>>
|
||
|
// which contains a reference to `xxx` and all the `T`s by value. Such a
|
||
|
// "view" is not cheap to copy, which is inconsistent with the usual
|
||
|
// expectations about views.
|
||
|
|
||
|
BOOST_HANA_NAMESPACE_BEGIN
|
||
|
|
||
|
namespace experimental {
|
||
|
struct view_tag;
|
||
|
|
||
|
namespace detail {
|
||
|
template <typename Sequence>
|
||
|
struct is_view {
|
||
|
static constexpr bool value = false;
|
||
|
};
|
||
|
|
||
|
template <typename Sequence>
|
||
|
using view_storage = typename std::conditional<
|
||
|
detail::is_view<Sequence>::value, Sequence, Sequence&
|
||
|
>::type;
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
// sliced_view
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
template <typename Sequence, std::size_t ...indices>
|
||
|
struct sliced_view_t {
|
||
|
detail::view_storage<Sequence> sequence_;
|
||
|
using hana_tag = view_tag;
|
||
|
};
|
||
|
|
||
|
template <typename Sequence, typename Indices>
|
||
|
constexpr auto sliced(Sequence& sequence, Indices const& indices) {
|
||
|
return hana::unpack(indices, [&](auto ...i) {
|
||
|
return sliced_view_t<Sequence, decltype(i)::value...>{sequence};
|
||
|
});
|
||
|
}
|
||
|
|
||
|
namespace detail {
|
||
|
template <typename Sequence, std::size_t ...i>
|
||
|
struct is_view<sliced_view_t<Sequence, i...>> {
|
||
|
static constexpr bool value = true;
|
||
|
};
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
// transformed_view
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
template <typename Sequence, typename F>
|
||
|
struct transformed_view_t {
|
||
|
detail::view_storage<Sequence> sequence_;
|
||
|
F f_;
|
||
|
using hana_tag = view_tag;
|
||
|
};
|
||
|
|
||
|
template <typename Sequence, typename F>
|
||
|
constexpr transformed_view_t<Sequence, typename hana::detail::decay<F>::type>
|
||
|
transformed(Sequence& sequence, F&& f) {
|
||
|
return {sequence, static_cast<F&&>(f)};
|
||
|
}
|
||
|
|
||
|
namespace detail {
|
||
|
template <typename Sequence, typename F>
|
||
|
struct is_view<transformed_view_t<Sequence, F>> {
|
||
|
static constexpr bool value = true;
|
||
|
};
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
// filtered_view
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
#if 0
|
||
|
template <typename Sequence, typename Pred>
|
||
|
using filtered_view_t = sliced_view_t<Sequence, detail::filtered_indices<...>>;
|
||
|
|
||
|
template <typename Sequence, typename Pred>
|
||
|
constexpr filtered_view_t<Sequence, Pred> filtered(Sequence& sequence, Pred&& pred) {
|
||
|
return {sequence};
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
// joined_view
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
template <typename Sequence1, typename Sequence2>
|
||
|
struct joined_view_t {
|
||
|
detail::view_storage<Sequence1> sequence1_;
|
||
|
detail::view_storage<Sequence2> sequence2_;
|
||
|
using hana_tag = view_tag;
|
||
|
};
|
||
|
|
||
|
struct make_joined_view_t {
|
||
|
template <typename Sequence1, typename Sequence2>
|
||
|
constexpr joined_view_t<Sequence1, Sequence2> operator()(Sequence1& s1, Sequence2& s2) const {
|
||
|
return {s1, s2};
|
||
|
}
|
||
|
};
|
||
|
constexpr make_joined_view_t joined{};
|
||
|
|
||
|
namespace detail {
|
||
|
template <typename Sequence1, typename Sequence2>
|
||
|
struct is_view<joined_view_t<Sequence1, Sequence2>> {
|
||
|
static constexpr bool value = true;
|
||
|
};
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
// single_view
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
template <typename T>
|
||
|
struct single_view_t {
|
||
|
T value_;
|
||
|
using hana_tag = view_tag;
|
||
|
};
|
||
|
|
||
|
template <typename T>
|
||
|
constexpr single_view_t<typename hana::detail::decay<T>::type> single_view(T&& t) {
|
||
|
return {static_cast<T&&>(t)};
|
||
|
}
|
||
|
|
||
|
namespace detail {
|
||
|
template <typename T>
|
||
|
struct is_view<single_view_t<T>> {
|
||
|
static constexpr bool value = true;
|
||
|
};
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
// empty_view
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
struct empty_view_t {
|
||
|
using hana_tag = view_tag;
|
||
|
};
|
||
|
|
||
|
constexpr empty_view_t empty_view() {
|
||
|
return {};
|
||
|
}
|
||
|
|
||
|
namespace detail {
|
||
|
template <>
|
||
|
struct is_view<empty_view_t> {
|
||
|
static constexpr bool value = true;
|
||
|
};
|
||
|
}
|
||
|
} // end namespace experimental
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
// Foldable
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
template <>
|
||
|
struct unpack_impl<experimental::view_tag> {
|
||
|
// sliced_view
|
||
|
template <typename Sequence, std::size_t ...i, typename F>
|
||
|
static constexpr decltype(auto)
|
||
|
apply(experimental::sliced_view_t<Sequence, i...> view, F&& f) {
|
||
|
(void)view; // Remove spurious unused variable warning with GCC
|
||
|
return static_cast<F&&>(f)(hana::at_c<i>(view.sequence_)...);
|
||
|
}
|
||
|
|
||
|
// transformed_view
|
||
|
template <typename Sequence, typename F, typename G>
|
||
|
static constexpr decltype(auto)
|
||
|
apply(experimental::transformed_view_t<Sequence, F> view, G&& g) {
|
||
|
return hana::unpack(view.sequence_, hana::on(static_cast<G&&>(g), view.f_));
|
||
|
}
|
||
|
|
||
|
// joined_view
|
||
|
template <typename View, typename F, std::size_t ...i1, std::size_t ...i2>
|
||
|
static constexpr decltype(auto)
|
||
|
unpack_joined(View view, F&& f, std::index_sequence<i1...>,
|
||
|
std::index_sequence<i2...>)
|
||
|
{
|
||
|
(void)view; // Remove spurious unused variable warning with GCC
|
||
|
return static_cast<F&&>(f)(hana::at_c<i1>(view.sequence1_)...,
|
||
|
hana::at_c<i2>(view.sequence2_)...);
|
||
|
}
|
||
|
|
||
|
template <typename S1, typename S2, typename F>
|
||
|
static constexpr decltype(auto)
|
||
|
apply(experimental::joined_view_t<S1, S2> view, F&& f) {
|
||
|
constexpr auto N1 = decltype(hana::length(view.sequence1_))::value;
|
||
|
constexpr auto N2 = decltype(hana::length(view.sequence2_))::value;
|
||
|
return unpack_joined(view, static_cast<F&&>(f),
|
||
|
std::make_index_sequence<N1>{},
|
||
|
std::make_index_sequence<N2>{});
|
||
|
}
|
||
|
|
||
|
// single_view
|
||
|
template <typename T, typename F>
|
||
|
static constexpr decltype(auto) apply(experimental::single_view_t<T> view, F&& f) {
|
||
|
return static_cast<F&&>(f)(view.value_);
|
||
|
}
|
||
|
|
||
|
// empty_view
|
||
|
template <typename F>
|
||
|
static constexpr decltype(auto) apply(experimental::empty_view_t, F&& f) {
|
||
|
return static_cast<F&&>(f)();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
// Iterable
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
template <>
|
||
|
struct at_impl<experimental::view_tag> {
|
||
|
// sliced_view
|
||
|
template <typename Sequence, std::size_t ...i, typename N>
|
||
|
static constexpr decltype(auto)
|
||
|
apply(experimental::sliced_view_t<Sequence, i...> view, N const&) {
|
||
|
constexpr std::size_t indices[] = {i...};
|
||
|
constexpr std::size_t n = indices[N::value];
|
||
|
return hana::at_c<n>(view.sequence_);
|
||
|
}
|
||
|
|
||
|
// transformed_view
|
||
|
template <typename Sequence, typename F, typename N>
|
||
|
static constexpr decltype(auto)
|
||
|
apply(experimental::transformed_view_t<Sequence, F> view, N const& n) {
|
||
|
return view.f_(hana::at(view.sequence_, n));
|
||
|
}
|
||
|
|
||
|
// joined_view
|
||
|
template <std::size_t Left, typename View, typename N>
|
||
|
static constexpr decltype(auto) at_joined_view(View view, N const&, hana::true_) {
|
||
|
return hana::at_c<N::value>(view.sequence1_);
|
||
|
}
|
||
|
|
||
|
template <std::size_t Left, typename View, typename N>
|
||
|
static constexpr decltype(auto) at_joined_view(View view, N const&, hana::false_) {
|
||
|
return hana::at_c<N::value - Left>(view.sequence2_);
|
||
|
}
|
||
|
|
||
|
template <typename S1, typename S2, typename N>
|
||
|
static constexpr decltype(auto)
|
||
|
apply(experimental::joined_view_t<S1, S2> view, N const& n) {
|
||
|
constexpr auto Left = decltype(hana::length(view.sequence1_))::value;
|
||
|
return at_joined_view<Left>(view, n, hana::bool_c<(N::value < Left)>);
|
||
|
}
|
||
|
|
||
|
// single_view
|
||
|
template <typename T, typename N>
|
||
|
static constexpr decltype(auto) apply(experimental::single_view_t<T> view, N const&) {
|
||
|
static_assert(N::value == 0,
|
||
|
"trying to fetch an out-of-bounds element in a hana::single_view");
|
||
|
return view.value_;
|
||
|
}
|
||
|
|
||
|
// empty_view
|
||
|
template <typename N>
|
||
|
static constexpr decltype(auto) apply(experimental::empty_view_t, N const&) = delete;
|
||
|
};
|
||
|
|
||
|
template <>
|
||
|
struct length_impl<experimental::view_tag> {
|
||
|
// sliced_view
|
||
|
template <typename Sequence, std::size_t ...i>
|
||
|
static constexpr auto
|
||
|
apply(experimental::sliced_view_t<Sequence, i...>) {
|
||
|
return hana::size_c<sizeof...(i)>;
|
||
|
}
|
||
|
|
||
|
// transformed_view
|
||
|
template <typename Sequence, typename F>
|
||
|
static constexpr auto apply(experimental::transformed_view_t<Sequence, F> view) {
|
||
|
return hana::length(view.sequence_);
|
||
|
}
|
||
|
|
||
|
// joined_view
|
||
|
template <typename S1, typename S2>
|
||
|
static constexpr auto apply(experimental::joined_view_t<S1, S2> view) {
|
||
|
return hana::size_c<
|
||
|
decltype(hana::length(view.sequence1_))::value +
|
||
|
decltype(hana::length(view.sequence2_))::value
|
||
|
>;
|
||
|
}
|
||
|
|
||
|
// single_view
|
||
|
template <typename T>
|
||
|
static constexpr auto apply(experimental::single_view_t<T>) {
|
||
|
return hana::size_c<1>;
|
||
|
}
|
||
|
|
||
|
// empty_view
|
||
|
static constexpr auto apply(experimental::empty_view_t) {
|
||
|
return hana::size_c<0>;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template <>
|
||
|
struct is_empty_impl<experimental::view_tag> {
|
||
|
// sliced_view
|
||
|
template <typename Sequence, std::size_t ...i>
|
||
|
static constexpr auto
|
||
|
apply(experimental::sliced_view_t<Sequence, i...>) {
|
||
|
return hana::bool_c<sizeof...(i) == 0>;
|
||
|
}
|
||
|
|
||
|
// transformed_view
|
||
|
template <typename Sequence, typename F>
|
||
|
static constexpr auto apply(experimental::transformed_view_t<Sequence, F> view) {
|
||
|
return hana::is_empty(view.sequence_);
|
||
|
}
|
||
|
|
||
|
// joined_view
|
||
|
template <typename S1, typename S2>
|
||
|
static constexpr auto apply(experimental::joined_view_t<S1, S2> view) {
|
||
|
return hana::and_(hana::is_empty(view.sequence1_),
|
||
|
hana::is_empty(view.sequence2_));
|
||
|
}
|
||
|
|
||
|
// single_view
|
||
|
template <typename T>
|
||
|
static constexpr auto apply(experimental::single_view_t<T>) {
|
||
|
return hana::false_c;
|
||
|
}
|
||
|
|
||
|
// empty_view
|
||
|
static constexpr auto apply(experimental::empty_view_t) {
|
||
|
return hana::true_c;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template <>
|
||
|
struct drop_front_impl<experimental::view_tag> {
|
||
|
template <typename View, typename N>
|
||
|
static constexpr auto apply(View view, N const&) {
|
||
|
constexpr auto n = N::value;
|
||
|
constexpr auto Length = decltype(hana::length(view))::value;
|
||
|
return experimental::sliced(view, hana::range_c<std::size_t, n, Length>);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
// Functor
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
template <>
|
||
|
struct transform_impl<experimental::view_tag> {
|
||
|
template <typename Sequence, typename F, typename G>
|
||
|
static constexpr auto
|
||
|
apply(experimental::transformed_view_t<Sequence, F> view, G&& g) {
|
||
|
return experimental::transformed(view.sequence_,
|
||
|
hana::compose(static_cast<G&&>(g), view.f_));
|
||
|
}
|
||
|
|
||
|
template <typename View, typename F>
|
||
|
static constexpr auto apply(View view, F&& f) {
|
||
|
return experimental::transformed(view, static_cast<F&&>(f));
|
||
|
}
|
||
|
};
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
// Applicative
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
template <>
|
||
|
struct lift_impl<experimental::view_tag> {
|
||
|
template <typename T>
|
||
|
static constexpr auto apply(T&& t) {
|
||
|
return experimental::single_view(static_cast<T&&>(t));
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template <>
|
||
|
struct ap_impl<experimental::view_tag> {
|
||
|
template <typename F, typename X>
|
||
|
static constexpr auto apply(F&& f, X&& x) {
|
||
|
// TODO: Implement cleverly; we most likely need a cartesian_product
|
||
|
// view or something like that.
|
||
|
return hana::ap(hana::to_tuple(f), hana::to_tuple(x));
|
||
|
}
|
||
|
};
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
// Monad
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
template <>
|
||
|
struct flatten_impl<experimental::view_tag> {
|
||
|
template <typename View>
|
||
|
static constexpr auto apply(View view) {
|
||
|
// TODO: Implement a flattened_view instead
|
||
|
return hana::fold_left(view, experimental::empty_view(),
|
||
|
experimental::joined);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
// MonadPlus
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
template <>
|
||
|
struct concat_impl<experimental::view_tag> {
|
||
|
template <typename View1, typename View2>
|
||
|
static constexpr auto apply(View1 view1, View2 view2) {
|
||
|
return experimental::joined(view1, view2);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template <>
|
||
|
struct empty_impl<experimental::view_tag> {
|
||
|
static constexpr auto apply() {
|
||
|
return experimental::empty_view();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
// Comparable
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
template <>
|
||
|
struct equal_impl<experimental::view_tag, experimental::view_tag> {
|
||
|
template <typename View1, typename View2>
|
||
|
static constexpr auto apply(View1 v1, View2 v2) {
|
||
|
// TODO: Use a lexicographical comparison algorithm.
|
||
|
return hana::equal(hana::to_tuple(v1), hana::to_tuple(v2));
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template <typename S>
|
||
|
struct equal_impl<experimental::view_tag, S, hana::when<hana::Sequence<S>::value>> {
|
||
|
template <typename View1, typename Seq>
|
||
|
static constexpr auto apply(View1 v1, Seq const& s) {
|
||
|
// TODO: Use a lexicographical comparison algorithm.
|
||
|
return hana::equal(hana::to_tuple(v1), hana::to_tuple(s));
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template <typename S>
|
||
|
struct equal_impl<S, experimental::view_tag, hana::when<hana::Sequence<S>::value>> {
|
||
|
template <typename Seq, typename View2>
|
||
|
static constexpr auto apply(Seq const& s, View2 v2) {
|
||
|
// TODO: Use a lexicographical comparison algorithm.
|
||
|
return hana::equal(hana::to_tuple(s), hana::to_tuple(v2));
|
||
|
}
|
||
|
};
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
// Orderable
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
template <>
|
||
|
struct less_impl<experimental::view_tag, experimental::view_tag> {
|
||
|
template <typename View1, typename View2>
|
||
|
static constexpr auto apply(View1 v1, View2 v2) {
|
||
|
return hana::lexicographical_compare(v1, v2);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template <typename S>
|
||
|
struct less_impl<experimental::view_tag, S, hana::when<hana::Sequence<S>::value>> {
|
||
|
template <typename View1, typename Seq>
|
||
|
static constexpr auto apply(View1 v1, Seq const& s) {
|
||
|
return hana::lexicographical_compare(v1, s);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template <typename S>
|
||
|
struct less_impl<S, experimental::view_tag, hana::when<hana::Sequence<S>::value>> {
|
||
|
template <typename Seq, typename View2>
|
||
|
static constexpr auto apply(Seq const& s, View2 v2) {
|
||
|
return hana::lexicographical_compare(s, v2);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
BOOST_HANA_NAMESPACE_END
|
||
|
|
||
|
#endif // !BOOST_HANA_EXPERIMENTAL_VIEW_HPP
|