From be8422d7a6f84eb238a7344351b0008ba24d79c9 Mon Sep 17 00:00:00 2001 From: Jason Rice Date: Sat, 1 Jul 2017 22:45:20 -0700 Subject: [PATCH] [view] cartesian_product - Adds cartesian_product_view_t and cartesian_product_element_view_t with specializations for the following: - at_impl - is_empty_impl - length_impl - unpack_impl --- include/boost/hana/cartesian_product.hpp | 75 ++++++++++++++--- include/boost/hana/view.hpp | 111 +++++++++++++++++++++++++- test/view/cartesian_product/at.cpp | 88 ++++++++++++++++++++ test/view/cartesian_product/is_empty.cpp | 71 ++++++++++++++++ test/view/cartesian_product/length.cpp | 86 ++++++++++++++++++++ test/view/cartesian_product/modifications.cpp | 39 +++++++++ test/view/cartesian_product/unpack.cpp | 111 ++++++++++++++++++++++++++ 7 files changed, 570 insertions(+), 11 deletions(-) create mode 100644 test/view/cartesian_product/at.cpp create mode 100644 test/view/cartesian_product/is_empty.cpp create mode 100644 test/view/cartesian_product/length.cpp create mode 100644 test/view/cartesian_product/modifications.cpp create mode 100644 test/view/cartesian_product/unpack.cpp diff --git a/include/boost/hana/cartesian_product.hpp b/include/boost/hana/cartesian_product.hpp index eaf8bb4d9..f4d9f322f 100644 --- a/include/boost/hana/cartesian_product.hpp +++ b/include/boost/hana/cartesian_product.hpp @@ -49,7 +49,7 @@ BOOST_HANA_NAMESPACE_BEGIN template struct cartesian_product_indices { static constexpr std::size_t total_length() { - std::size_t lengths[] = {Lengths...}; + std::size_t lengths[sizeof...(Lengths)] = {Lengths...}; std::size_t r = 1; for (std::size_t len: lengths) r *= len; @@ -59,9 +59,12 @@ BOOST_HANA_NAMESPACE_BEGIN static constexpr std::size_t length = total_length(); static constexpr auto indices_of(std::size_t i) { - constexpr std::size_t lengths[] = {Lengths...}; constexpr std::size_t n = sizeof...(Lengths); + constexpr std::size_t lengths[n] = {Lengths...}; detail::array result{}; + if (length == 0) { + return result; + } for (std::size_t j = n; j--;) { result[j] = i % lengths[j]; i /= lengths[j]; @@ -69,21 +72,72 @@ BOOST_HANA_NAMESPACE_BEGIN return result; } - template + template static constexpr auto - product_element(std::index_sequence, Xs&& ...xs) { + product_element(std::index_sequence, F&& f, Xs&& ...xs) { constexpr auto indices = indices_of(n); - return hana::make(hana::at_c(xs)...); + return static_cast(f)(hana::at_c(xs)...); } - template + template static constexpr auto - create_product(std::index_sequence, Xs&& ...xs) { - return hana::make(product_element( - std::make_index_sequence{}, xs... + create_product(std::index_sequence, F&& f, Xs&& ...xs) { + return F{}(product_element( + std::make_index_sequence{}, + static_cast(f), xs... )...); } }; + + template <> + struct cartesian_product_indices<> { + static constexpr std::size_t total_length() + { return 0; } + + static constexpr std::size_t length = 0; + }; + + struct make_cartesian_product_indices_helper_t { + template + constexpr auto operator()(Xs&& ...xs) const + -> detail::cartesian_product_indices< + decltype(hana::length(xs))::value... + > + { return {}; } + }; + + struct make_cartesian_product_indices_t { + template + constexpr auto operator()(Xs&& xs) const { + return hana::unpack( + static_cast(xs), + make_cartesian_product_indices_helper_t{}); + } + }; + + constexpr make_cartesian_product_indices_t make_cartesian_product_indices{}; + + template + struct make_cartesian_product_element_t { + F const& f; + + constexpr auto operator()() const { + return f(); + } + + template + constexpr auto operator()(X1&& x1, Xs&& ...xs) const { + constexpr auto indices = detail::cartesian_product_indices< + decltype(hana::length(x1))::value, + decltype(hana::length(xs))::value... + >{}; + return indices.template product_element( + std::make_index_sequence{}, f, + static_cast(x1), + static_cast(xs)... + ); + } + }; } // Credits: implementation adapted from http://github.com/alexk7/hel. @@ -99,8 +153,9 @@ BOOST_HANA_NAMESPACE_BEGIN using indices = detail::cartesian_product_indices< decltype(hana::length(xs))::value... >; - return indices::template create_product( + return indices::template create_product( std::make_index_sequence{}, + hana::make, static_cast(xs)...); } diff --git a/include/boost/hana/view.hpp b/include/boost/hana/view.hpp index 93b2d90fe..1e4bbc225 100644 --- a/include/boost/hana/view.hpp +++ b/include/boost/hana/view.hpp @@ -16,6 +16,7 @@ Distributed under the Boost Software License, Version 1.0. #include #include #include +#include #include #include #include @@ -59,6 +60,43 @@ BOOST_HANA_NAMESPACE_BEGIN >::type; ////////////////////////////////////////////////////////////////////// + // cartesian_product_element_view + ////////////////////////////////////////////////////////////////////// + template + struct cartesian_product_element_view_t { + detail::view_storage sequences_; + using hana_tag = view_tag; + }; + + template + struct is_view> { + static constexpr bool value = true; + }; + + ////////////////////////////////////////////////////////////////////// + // cartesian_product_view + ////////////////////////////////////////////////////////////////////// + template + struct cartesian_product_view_t { + detail::view_storage sequences_; + using hana_tag = view_tag; + }; + + struct make_cartesian_product_view_t { + template + constexpr cartesian_product_view_t operator()(Sequences& s) const { + return {s}; + } + }; + + constexpr make_cartesian_product_view_t cartesian_product_view{}; + + template + struct is_view> { + static constexpr bool value = true; + }; + + ////////////////////////////////////////////////////////////////////// // sliced_view ////////////////////////////////////////////////////////////////////// template @@ -212,6 +250,33 @@ BOOST_HANA_NAMESPACE_BEGIN ////////////////////////////////////////////////////////////////////////// template <> struct unpack_impl { + // cartesian_product_element_view + template + static constexpr decltype(auto) + apply(detail::cartesian_product_element_view_t view, F const& f) { + using Indices = decltype(detail::make_cartesian_product_indices(view.sequences_)); + return hana::unpack(view.sequences_, + detail::make_cartesian_product_element_t{f}); + } + + // cartesian_product_view + template + static constexpr decltype(auto) + unpack_cartesian_product(View view, F&& f, std::index_sequence) { + return static_cast(f)( + detail::cartesian_product_element_view_t{ + view.sequences_ + }...); + } + + template + static constexpr decltype(auto) + apply(detail::cartesian_product_view_t view, F&& f) { + using Indices = decltype(detail::make_cartesian_product_indices(view.sequences_)); + return unpack_cartesian_product(view, static_cast(f), + std::make_index_sequence{}); + } + // sliced_view template static constexpr decltype(auto) @@ -279,6 +344,22 @@ BOOST_HANA_NAMESPACE_BEGIN ////////////////////////////////////////////////////////////////////////// template <> struct at_impl { + // cartesian_product_element_view + template + static constexpr decltype(auto) + apply(detail::cartesian_product_element_view_t view, N const&) { + using Indices = decltype(detail::make_cartesian_product_indices(view.sequences_)); + return hana::at_c( + hana::at_c(view.sequences_)); + } + + // cartesian_product_view + template + static constexpr decltype(auto) + apply(detail::cartesian_product_view_t view, N const&) { + return detail::cartesian_product_element_view_t{view.sequences_}; + } + // sliced_view template static constexpr decltype(auto) @@ -368,6 +449,19 @@ BOOST_HANA_NAMESPACE_BEGIN template <> struct length_impl { + // cartesian_product_view_element + template + static constexpr auto apply(detail::cartesian_product_element_view_t view) { + return hana::length(view.sequences_); + } + + // cartesian_product_view + template + static constexpr auto apply(detail::cartesian_product_view_t view) { + using Indices = decltype(detail::make_cartesian_product_indices(view.sequences_)); + return hana::size_c; + } + // sliced_view template static constexpr auto @@ -394,11 +488,12 @@ BOOST_HANA_NAMESPACE_BEGIN struct sum_lengths { template constexpr auto operator()(S const& ...s) const { - constexpr std::size_t lengths[] = {decltype(hana::length(s))::value...}; + constexpr std::size_t lengths[sizeof...(S)] = {decltype(hana::length(s))::value...}; constexpr std::size_t sum = detail::accumulate(lengths, lengths+sizeof...(S), 0); return hana::size_t{}; } }; + template static constexpr auto apply(detail::flattened_view_t view) { return hana::unpack(view.sequences_, sum_lengths{}); @@ -424,6 +519,20 @@ BOOST_HANA_NAMESPACE_BEGIN template <> struct is_empty_impl { + // cartesian_product_view_element + template + static constexpr auto apply(detail::cartesian_product_element_view_t view) { + using Indices = decltype(detail::make_cartesian_product_indices(view.sequences_)); + return hana::bool_c; + } + + // cartesian_product_view + template + static constexpr auto apply(detail::cartesian_product_view_t view) { + using Indices = decltype(detail::make_cartesian_product_indices(view.sequences_)); + return hana::bool_c; + } + // sliced_view template static constexpr auto diff --git a/test/view/cartesian_product/at.cpp b/test/view/cartesian_product/at.cpp new file mode 100644 index 000000000..4e050d60e --- /dev/null +++ b/test/view/cartesian_product/at.cpp @@ -0,0 +1,88 @@ +// Copyright Louis Dionne 2013-2016 +// Copyright Jason Rice 2017 +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include + +#include +#include +namespace hana = boost::hana; +using hana::test::ct_eq; + + +int main() { + auto container = ::seq; + + { + const auto storage = container( + container(ct_eq<0>{}) + ); + + auto view = hana::detail::cartesian_product_view(storage); + BOOST_HANA_CONSTANT_CHECK(hana::equal( + hana::at(view, hana::size_c<0>), + container(ct_eq<0>{}) + )); + } + + { + const auto storage = container( + container(ct_eq<0>{}), + container(ct_eq<0>{}) + ); + + auto view = hana::detail::cartesian_product_view(storage); + BOOST_HANA_CONSTANT_CHECK(hana::equal( + hana::at(view, hana::size_c<0>), + container(ct_eq<0>{}, ct_eq<0>{}) + )); + } + + { + const auto storage = container( + container(ct_eq<0>{}, ct_eq<1>{}, ct_eq<2>{}), + container(ct_eq<3>{}, ct_eq<4>{}) + ); + + auto view = hana::detail::cartesian_product_view(storage); + + auto expected = container( + container(ct_eq<0>{}, ct_eq<3>{}), + container(ct_eq<0>{}, ct_eq<4>{}), + container(ct_eq<1>{}, ct_eq<3>{}), + container(ct_eq<1>{}, ct_eq<4>{}), + container(ct_eq<2>{}, ct_eq<3>{}), + container(ct_eq<2>{}, ct_eq<4>{}) + ); + + BOOST_HANA_CONSTANT_CHECK(hana::equal( + hana::at(view, hana::size_c<0>), + hana::at(expected, hana::size_c<0>) + )); + BOOST_HANA_CONSTANT_CHECK(hana::equal( + hana::at(view, hana::size_c<1>), + hana::at(expected, hana::size_c<1>) + )); + BOOST_HANA_CONSTANT_CHECK(hana::equal( + hana::at(view, hana::size_c<2>), + hana::at(expected, hana::size_c<2>) + )); + BOOST_HANA_CONSTANT_CHECK(hana::equal( + hana::at(view, hana::size_c<3>), + hana::at(expected, hana::size_c<3>) + )); + BOOST_HANA_CONSTANT_CHECK(hana::equal( + hana::at(view, hana::size_c<4>), + hana::at(expected, hana::size_c<4>) + )); + BOOST_HANA_CONSTANT_CHECK(hana::equal( + hana::at(view, hana::size_c<5>), + hana::at(expected, hana::size_c<5>) + )); + } +} diff --git a/test/view/cartesian_product/is_empty.cpp b/test/view/cartesian_product/is_empty.cpp new file mode 100644 index 000000000..aab59855e --- /dev/null +++ b/test/view/cartesian_product/is_empty.cpp @@ -0,0 +1,71 @@ +// Copyright Louis Dionne 2013-2016 +// Copyright Jason Rice 2017 +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include + +#include +#include +namespace hana = boost::hana; +using hana::test::ct_eq; + + +int main() { + auto container = ::seq; + + { + auto storage = container(container()); + auto view = hana::detail::cartesian_product_view(storage); + BOOST_HANA_CONSTANT_CHECK(hana::is_empty(view)); + } + + { + auto storage = container(container(ct_eq<0>{}), container()); + auto view = hana::detail::cartesian_product_view(storage); + BOOST_HANA_CONSTANT_CHECK(hana::is_empty(view)); + } + + { + auto storage = container( + container(ct_eq<0>{}), + container(ct_eq<1>{}), + container(ct_eq<2>{}, ct_eq<3>{}), + container(), + container(ct_eq<4>{}) + ); + auto view = hana::detail::cartesian_product_view(storage); + BOOST_HANA_CONSTANT_CHECK(hana::is_empty(view)); + } + + { + auto storage = container(container(ct_eq<0>{})); + auto view = hana::detail::cartesian_product_view(storage); + BOOST_HANA_CONSTANT_CHECK(hana::not_(hana::is_empty(view))); + } + + { + auto storage = container( + container(ct_eq<0>{}), + container(ct_eq<1>{}), + container(ct_eq<2>{}, ct_eq<3>{}), + container(ct_eq<4>{}) + ); + auto view = hana::detail::cartesian_product_view(storage); + BOOST_HANA_CONSTANT_CHECK(hana::not_(hana::is_empty(view))); + } + + { + auto storage = container( + container(ct_eq<0>{}), + container(ct_eq<1>{}), + container(ct_eq<2>{}, ct_eq<3>{}), + container(ct_eq<4>{}, ct_eq<5>{}, ct_eq<6>{}) + ); + auto view = hana::detail::cartesian_product_view(storage); + BOOST_HANA_CONSTANT_CHECK(hana::not_(hana::is_empty(view))); + } +} diff --git a/test/view/cartesian_product/length.cpp b/test/view/cartesian_product/length.cpp new file mode 100644 index 000000000..86a88cab7 --- /dev/null +++ b/test/view/cartesian_product/length.cpp @@ -0,0 +1,86 @@ +// Copyright Louis Dionne 2013-2016 +// Copyright Jason Rice 2017 +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include + +#include +#include +namespace hana = boost::hana; +using hana::test::ct_eq; + + +int main() { + auto container = ::seq; + + { + auto storage = container(container()); + auto view = hana::detail::cartesian_product_view(storage); + BOOST_HANA_CONSTANT_CHECK(hana::equal( + hana::length(view), + hana::size_c<0> + )); + } + + { + auto storage = container(container(ct_eq<0>{}), container()); + auto view = hana::detail::cartesian_product_view(storage); + BOOST_HANA_CONSTANT_CHECK(hana::is_empty(view)); + } + + { + auto storage = container( + container(ct_eq<0>{}), + container(ct_eq<1>{}), + container(ct_eq<2>{}, ct_eq<3>{}), + container(), + container(ct_eq<4>{}) + ); + auto view = hana::detail::cartesian_product_view(storage); + BOOST_HANA_CONSTANT_CHECK(hana::equal( + hana::length(view), + hana::size_c<0> + )); + } + + { + auto storage = container(container(ct_eq<0>{})); + auto view = hana::detail::cartesian_product_view(storage); + BOOST_HANA_CONSTANT_CHECK(hana::equal( + hana::length(view), + hana::size_c<1> + )); + } + + { + auto storage = container( + container(ct_eq<0>{}), + container(ct_eq<1>{}), + container(ct_eq<2>{}, ct_eq<3>{}), + container(ct_eq<4>{}) + ); + auto view = hana::detail::cartesian_product_view(storage); + BOOST_HANA_CONSTANT_CHECK(hana::equal( + hana::length(view), + hana::size_c<2> + )); + } + + { + auto storage = container( + container(ct_eq<0>{}), + container(ct_eq<1>{}), + container(ct_eq<2>{}, ct_eq<3>{}), + container(ct_eq<4>{}, ct_eq<5>{}, ct_eq<6>{}) + ); + auto view = hana::detail::cartesian_product_view(storage); + BOOST_HANA_CONSTANT_CHECK(hana::equal( + hana::length(view), + hana::size_c<6> + )); + } +} diff --git a/test/view/cartesian_product/modifications.cpp b/test/view/cartesian_product/modifications.cpp new file mode 100644 index 000000000..b2ec00506 --- /dev/null +++ b/test/view/cartesian_product/modifications.cpp @@ -0,0 +1,39 @@ +// Copyright Louis Dionne 2013-2016 +// Copyright Jason Rice 2017 +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + +#include +#include +#include + +#include +namespace hana = boost::hana; + + +int main() { + auto container = ::seq; + + auto storage = container(container(0), + container(1), + container(2, 3), + container(4, 5, 6)); + + auto view = hana::detail::cartesian_product_view(storage); + + hana::at_c<0>(hana::at_c<0>(view)) = 90; + hana::at_c<1>(hana::at_c<0>(view)) = 91; + hana::at_c<2>(hana::at_c<0>(view)) = 92; + hana::at_c<3>(hana::at_c<0>(view)) = 94; + hana::at_c<3>(hana::at_c<1>(view)) = 95; + hana::at_c<2>(hana::at_c<5>(view)) = 93; + hana::at_c<3>(hana::at_c<5>(view)) = 96; + + BOOST_HANA_RUNTIME_CHECK(hana::equal( + storage, + container(container(90), + container(91), + container(92, 93), + container(94, 95, 96)) + )); +} diff --git a/test/view/cartesian_product/unpack.cpp b/test/view/cartesian_product/unpack.cpp new file mode 100644 index 000000000..4519afd3a --- /dev/null +++ b/test/view/cartesian_product/unpack.cpp @@ -0,0 +1,111 @@ +// Copyright Louis Dionne 2013-2016 +// Copyright Jason Rice 2017 +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + +#include +#include +#include + +#include +#include +namespace hana = boost::hana; +using hana::test::ct_eq; + + +int main() { + auto container = ::seq; + auto make_container = [](auto ...x) { + return ::seq(x...); + }; + + { + auto storage = container(); + auto view = hana::detail::cartesian_product_view(storage); + BOOST_HANA_CONSTANT_CHECK(hana::is_empty(view)); + BOOST_HANA_CONSTANT_CHECK(hana::is_empty(hana::unpack(view, make_container))); + } + + { + auto storage = container(container()); + auto view = hana::detail::cartesian_product_view(storage); + BOOST_HANA_CONSTANT_CHECK(hana::is_empty(view)); + BOOST_HANA_CONSTANT_CHECK(hana::is_empty(hana::unpack(view, make_container))); + } + + { + auto storage = container(container(), container(), container()); + auto view = hana::detail::cartesian_product_view(storage); + BOOST_HANA_CONSTANT_CHECK(hana::is_empty(view)); + BOOST_HANA_CONSTANT_CHECK(hana::is_empty(hana::unpack(view, make_container))); + } + + { + auto storage = container(container(ct_eq<0>{}), container()); + auto view = hana::detail::cartesian_product_view(storage); + BOOST_HANA_CONSTANT_CHECK(hana::is_empty(view)); + BOOST_HANA_CONSTANT_CHECK(hana::is_empty(hana::unpack(view, make_container))); + } + + { + auto storage = container( + container(ct_eq<0>{}), + container(ct_eq<1>{}), + container(ct_eq<2>{}, ct_eq<3>{}), + container(), + container(ct_eq<4>{}) + ); + auto view = hana::detail::cartesian_product_view(storage); + BOOST_HANA_CONSTANT_CHECK(hana::equal( + hana::unpack(view, make_container), + container() + )); + } + + { + auto storage = container(container(ct_eq<0>{})); + auto view = hana::detail::cartesian_product_view(storage); + BOOST_HANA_CONSTANT_CHECK(hana::equal( + hana::unpack(view, make_container), + container(container(ct_eq<0>{})) + )); + } + + { + auto storage = container( + container(ct_eq<0>{}), + container(ct_eq<1>{}), + container(ct_eq<2>{}, ct_eq<3>{}), + container(ct_eq<4>{}) + ); + auto view = hana::detail::cartesian_product_view(storage); + BOOST_HANA_CONSTANT_CHECK(hana::equal( + hana::unpack(view, make_container), + container( + container(ct_eq<0>{}, ct_eq<1>{}, ct_eq<2>{}, ct_eq<4>{}), + container(ct_eq<0>{}, ct_eq<1>{}, ct_eq<3>{}, ct_eq<4>{}) + ) + )); + } + + { + auto storage = container( + container(ct_eq<0>{}), + container(ct_eq<1>{}), + container(ct_eq<2>{}, ct_eq<3>{}), + container(ct_eq<4>{}, ct_eq<5>{}, ct_eq<6>{}) + ); + auto view = hana::detail::cartesian_product_view(storage); + BOOST_HANA_CONSTANT_CHECK(hana::equal( + hana::unpack(view, make_container), + container( + container(ct_eq<0>{}, ct_eq<1>{}, ct_eq<2>{}, ct_eq<4>{}), + container(ct_eq<0>{}, ct_eq<1>{}, ct_eq<2>{}, ct_eq<5>{}), + container(ct_eq<0>{}, ct_eq<1>{}, ct_eq<2>{}, ct_eq<6>{}), + container(ct_eq<0>{}, ct_eq<1>{}, ct_eq<3>{}, ct_eq<4>{}), + container(ct_eq<0>{}, ct_eq<1>{}, ct_eq<3>{}, ct_eq<5>{}), + container(ct_eq<0>{}, ct_eq<1>{}, ct_eq<3>{}, ct_eq<6>{}) + ) + )); + } +}