/* @file Defines experimental views. Copyright Louis Dionne 2013-2022 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // 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, joint_view, ....>>> // 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. namespace boost { namespace hana { namespace experimental { struct view_tag; namespace detail { template struct is_view { static constexpr bool value = false; }; template using view_storage = typename std::conditional< detail::is_view::value, Sequence, Sequence& >::type; } ////////////////////////////////////////////////////////////////////////// // sliced_view ////////////////////////////////////////////////////////////////////////// template struct sliced_view_t { detail::view_storage sequence_; using hana_tag = view_tag; }; template constexpr auto sliced(Sequence& sequence, Indices const& indices) { return hana::unpack(indices, [&](auto ...i) { return sliced_view_t{sequence}; }); } namespace detail { template struct is_view> { static constexpr bool value = true; }; } ////////////////////////////////////////////////////////////////////////// // transformed_view ////////////////////////////////////////////////////////////////////////// template struct transformed_view_t { detail::view_storage sequence_; F f_; using hana_tag = view_tag; }; template constexpr transformed_view_t::type> transformed(Sequence& sequence, F&& f) { return {sequence, static_cast(f)}; } namespace detail { template struct is_view> { static constexpr bool value = true; }; } ////////////////////////////////////////////////////////////////////////// // filtered_view ////////////////////////////////////////////////////////////////////////// #if 0 template using filtered_view_t = sliced_view_t>; template constexpr filtered_view_t filtered(Sequence& sequence, Pred&& pred) { return {sequence}; } #endif ////////////////////////////////////////////////////////////////////////// // joined_view ////////////////////////////////////////////////////////////////////////// template struct joined_view_t { detail::view_storage sequence1_; detail::view_storage sequence2_; using hana_tag = view_tag; }; struct make_joined_view_t { template constexpr joined_view_t operator()(Sequence1& s1, Sequence2& s2) const { return {s1, s2}; } }; BOOST_HANA_INLINE_VARIABLE constexpr make_joined_view_t joined{}; namespace detail { template struct is_view> { static constexpr bool value = true; }; } ////////////////////////////////////////////////////////////////////////// // single_view ////////////////////////////////////////////////////////////////////////// template struct single_view_t { T value_; using hana_tag = view_tag; }; template constexpr single_view_t::type> single_view(T&& t) { return {static_cast(t)}; } namespace detail { template struct is_view> { 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 { static constexpr bool value = true; }; } } // end namespace experimental ////////////////////////////////////////////////////////////////////////// // Foldable ////////////////////////////////////////////////////////////////////////// template <> struct unpack_impl { // sliced_view template static constexpr decltype(auto) apply(experimental::sliced_view_t view, F&& f) { (void)view; // Remove spurious unused variable warning with GCC return static_cast(f)(hana::at_c(view.sequence_)...); } // transformed_view template static constexpr decltype(auto) apply(experimental::transformed_view_t view, G&& g) { return hana::unpack(view.sequence_, hana::on(static_cast(g), view.f_)); } // joined_view template static constexpr decltype(auto) unpack_joined(View view, F&& f, std::index_sequence, std::index_sequence) { (void)view; // Remove spurious unused variable warning with GCC return static_cast(f)(hana::at_c(view.sequence1_)..., hana::at_c(view.sequence2_)...); } template static constexpr decltype(auto) apply(experimental::joined_view_t 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), std::make_index_sequence{}, std::make_index_sequence{}); } // single_view template static constexpr decltype(auto) apply(experimental::single_view_t view, F&& f) { return static_cast(f)(view.value_); } // empty_view template static constexpr decltype(auto) apply(experimental::empty_view_t, F&& f) { return static_cast(f)(); } }; ////////////////////////////////////////////////////////////////////////// // Iterable ////////////////////////////////////////////////////////////////////////// template <> struct at_impl { // sliced_view template static constexpr decltype(auto) apply(experimental::sliced_view_t view, N const&) { constexpr std::size_t indices[] = {i...}; constexpr std::size_t n = indices[N::value]; return hana::at_c(view.sequence_); } // transformed_view template static constexpr decltype(auto) apply(experimental::transformed_view_t view, N const& n) { return view.f_(hana::at(view.sequence_, n)); } // joined_view template static constexpr decltype(auto) at_joined_view(View view, N const&, hana::true_) { return hana::at_c(view.sequence1_); } template static constexpr decltype(auto) at_joined_view(View view, N const&, hana::false_) { return hana::at_c(view.sequence2_); } template static constexpr decltype(auto) apply(experimental::joined_view_t view, N const& n) { constexpr auto Left = decltype(hana::length(view.sequence1_))::value; return at_joined_view(view, n, hana::bool_c<(N::value < Left)>); } // single_view template static constexpr decltype(auto) apply(experimental::single_view_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 static constexpr decltype(auto) apply(experimental::empty_view_t, N const&) = delete; }; template <> struct length_impl { // sliced_view template static constexpr auto apply(experimental::sliced_view_t) { return hana::size_c; } // transformed_view template static constexpr auto apply(experimental::transformed_view_t view) { return hana::length(view.sequence_); } // joined_view template static constexpr auto apply(experimental::joined_view_t view) { return hana::size_c< decltype(hana::length(view.sequence1_))::value + decltype(hana::length(view.sequence2_))::value >; } // single_view template static constexpr auto apply(experimental::single_view_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 { // sliced_view template static constexpr auto apply(experimental::sliced_view_t) { return hana::bool_c; } // transformed_view template static constexpr auto apply(experimental::transformed_view_t view) { return hana::is_empty(view.sequence_); } // joined_view template static constexpr auto apply(experimental::joined_view_t view) { return hana::and_(hana::is_empty(view.sequence1_), hana::is_empty(view.sequence2_)); } // single_view template static constexpr auto apply(experimental::single_view_t) { return hana::false_c; } // empty_view static constexpr auto apply(experimental::empty_view_t) { return hana::true_c; } }; template <> struct drop_front_impl { template 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); } }; ////////////////////////////////////////////////////////////////////////// // Functor ////////////////////////////////////////////////////////////////////////// template <> struct transform_impl { template static constexpr auto apply(experimental::transformed_view_t view, G&& g) { return experimental::transformed(view.sequence_, hana::compose(static_cast(g), view.f_)); } template static constexpr auto apply(View view, F&& f) { return experimental::transformed(view, static_cast(f)); } }; ////////////////////////////////////////////////////////////////////////// // Applicative ////////////////////////////////////////////////////////////////////////// template <> struct lift_impl { template static constexpr auto apply(T&& t) { return experimental::single_view(static_cast(t)); } }; template <> struct ap_impl { template 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 { template 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 { template static constexpr auto apply(View1 view1, View2 view2) { return experimental::joined(view1, view2); } }; template <> struct empty_impl { static constexpr auto apply() { return experimental::empty_view(); } }; ////////////////////////////////////////////////////////////////////////// // Comparable ////////////////////////////////////////////////////////////////////////// template <> struct equal_impl { template 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 struct equal_impl::value>> { template 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 struct equal_impl::value>> { template 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 { template static constexpr auto apply(View1 v1, View2 v2) { return hana::lexicographical_compare(v1, v2); } }; template struct less_impl::value>> { template static constexpr auto apply(View1 v1, Seq const& s) { return hana::lexicographical_compare(v1, s); } }; template struct less_impl::value>> { template static constexpr auto apply(Seq const& s, View2 v2) { return hana::lexicographical_compare(s, v2); } }; }} // end namespace boost::hana #endif // !BOOST_HANA_EXPERIMENTAL_VIEW_HPP