diff --git a/core/include/detray/utils/ranges/chain.hpp b/core/include/detray/utils/ranges/chain.hpp index 66a7cbda59..bdc3f6284a 100644 --- a/core/include/detray/utils/ranges/chain.hpp +++ b/core/include/detray/utils/ranges/chain.hpp @@ -24,7 +24,7 @@ struct chain_iterator; } -/// @brief Chain together different ranges of the same type. +/// @brief Range adaptor that chains together different ranges of the same type. /// /// @tparam I the number of ranges in the chain. /// @tparam range_itr_t the iterator type of the ranges. @@ -177,10 +177,7 @@ struct chain_iterator { DETRAY_HOST_DEVICE constexpr chain_iterator(const iterator_coll_t &begins, const iterator_coll_t &ends) - : m_begins(&begins), - m_ends(&ends), - m_iter{detray::detail::get<0>(*m_begins)}, - m_idx{0} {} + : m_begins(&begins), m_ends(&ends), m_iter{(*m_begins)[0]}, m_idx{0} {} /// Fully parametrized construction DETRAY_HOST_DEVICE diff --git a/core/include/detray/utils/ranges/enumerate.hpp b/core/include/detray/utils/ranges/enumerate.hpp index 5fa9f4ec51..5e4078c36a 100644 --- a/core/include/detray/utils/ranges/enumerate.hpp +++ b/core/include/detray/utils/ranges/enumerate.hpp @@ -178,7 +178,7 @@ class enumerate_view : public detray::ranges::view_interface< DETRAY_HOST_DEVICE constexpr explicit enumerate_view(range_t &&rng) : m_begin{detray::ranges::begin(std::forward(rng)), 0}, m_end{detray::ranges::end(std::forward(rng)), - rng.size() - detray::ranges::range_difference_t{1}} {} + rng.size() - dindex{1}} {} /// Construct from a @param range that will be enumerated beginning at /// @param start. @@ -189,8 +189,7 @@ class enumerate_view : public detray::ranges::view_interface< dindex start) : m_begin{detray::ranges::begin(std::forward(rng)), start}, m_end{detray::ranges::end(std::forward(rng)), - start + rng.size() - - detray::ranges::range_difference_t{1}} {} + start + rng.size() - dindex{1}} {} /// Copy assignment operator DETRAY_HOST_DEVICE @@ -232,7 +231,8 @@ struct enumerate : public enumerate_view { template < typename range_t, std::enable_if_t::value, bool> = true> - DETRAY_HOST_DEVICE constexpr enumerate(range_t &&rng, dindex start) + DETRAY_HOST_DEVICE constexpr enumerate( + range_t &&rng, detray::ranges::range_difference_t start) : base_type(std::forward(rng), start) {} /// Construct from a @param range and an index range provided by a volume diff --git a/core/include/detray/utils/ranges/pick.hpp b/core/include/detray/utils/ranges/pick.hpp index bb828bd521..eecedac4c0 100644 --- a/core/include/detray/utils/ranges/pick.hpp +++ b/core/include/detray/utils/ranges/pick.hpp @@ -18,7 +18,8 @@ namespace detray::ranges { -/// @brief Indexes the elements of a range by iterating through an index range. +/// @brief Range adaptor that indexes the elements of a range using another +/// index range. /// /// @tparam range_itr_t the iterator type of the enumerated range /// @tparam sequence_itr_t range of indices. diff --git a/core/include/detray/utils/ranges/ranges.hpp b/core/include/detray/utils/ranges/ranges.hpp index a385a2125f..d15928ba92 100644 --- a/core/include/detray/utils/ranges/ranges.hpp +++ b/core/include/detray/utils/ranges/ranges.hpp @@ -15,101 +15,179 @@ namespace detray::ranges { -/// @brief Detail interface of an iterable type. +/// @brief Provides c++17 detray iterators in std::ranges style, meant to be +//// used in device code. /// -/// Base case: The type is not iterable -template -struct range : public std::false_type { - using type = void; -}; - -/// @brief Check if a type is iterable and extract iterator type. +/// @note Does make use of concepts and des not implement full ranges standard +/// (e.g. not every requirement is modelled, range/view complexity guarantees +/// are not given and there is no lazy-evaluation). +/// missing: ranges::sized_range, ranges::viewable_range, ranges::constant_range /// -/// A range has a begin() and an end() member function, which is guranteed by -/// simply calling std::begin and std::end. -/// In case of @c vecmem::device_vector the iterator is a pointer type. -template -struct range< - T, std::enable_if_t()))>> or - std::is_pointer_v()))>>, - void>> : public std::true_type { - using iterator_type = - std::decay_t()))>; - using const_iterator_type = - std::decay_t()))>; -}; - -class base_view; - -template -class view_interface; - -template -inline constexpr bool enable_view = - std::is_base_of_v or std::is_base_of_v, T>; - -template -inline constexpr bool is_view = detray::ranges::range::value and - std::is_object_v&& std::is_move_constructible_vand enable_view; +/// @see https://en.cppreference.com/w/cpp/ranges +/// @{ -/// Simply import the std versions of basic iterator functionality +/// Simply import the std versions of basic iterator functionality for now /// @{ using std::begin; using std::cbegin; using std::cend; +using std::crbegin; +using std::crend; using std::end; using std::rbegin; using std::rend; +using std::size; +// ranges::ssize; +using std::data; +using std::empty; +// ranges::cdata + using std::advance; using std::distance; using std::next; using std::prev; /// @} -/// Implementation of the detray ranges specific functions (size, data) -/// @{ -using std::size; - -/// @} - -/// Compliance with std::ranges typedefs, +/// Ranges typedefs, /// @see https://en.cppreference.com/w/cpp/ranges/iterator_t /// @{ -template -using iterator_t = decltype(detray::ranges::begin(std::declval())); +template +using iterator_t = decltype(detray::ranges::begin(std::declval())); -template -using const_iterator_t = iterator_t; +template +using const_iterator_t = iterator_t; -template -using sentinel_t = decltype(detray::ranges::end(std::declval())); +template +using sentinel_t = decltype(detray::ranges::end(std::declval())); -template -using range_size_t = decltype(detray::ranges::size(std::declval())); +template +using range_size_t = decltype(detray::ranges::size(std::declval())); -template +template using range_difference_t = typename std::iterator_traits< - detray::ranges::iterator_t>::difference_type; + detray::ranges::iterator_t>::difference_type; -template +template using range_value_t = - typename std::iterator_traits>::value_type; + typename std::iterator_traits>::value_type; -template +template using range_reference_t = - typename std::iterator_traits>::reference; + typename std::iterator_traits>::reference; + +template +using range_const_reference_t = const range_reference_t; -template -using range_const_reference_t = const range_reference_t; +template +using range_rvalue_reference_t = std::decay_t>&&; -template -using range_rvalue_reference_t = std::decay_t>&&; /// @} -// https://en.cppreference.com/w/cpp/ranges/view +/// Range traits +/// @{ + +/// Base case: The type is not a 'range' +template +struct range : public std::false_type { + using type = void; +}; + +/// @brief A range has a begin() and an end() member function +/// +/// @see https://en.cppreference.com/w/cpp/ranges/range +/// +/// Existance of 'begin' and 'end' is guranteed by simply calling std::begin +/// and std::end. +/// @note In case of @c vecmem::device_vector the iterator is a pointer type. +template +struct range< + R, std::enable_if_t< + (std::is_class_v()))>> or + std::is_pointer_v()))>>)and(std:: + is_class_v()))>> or + std::is_pointer_v()))>>), + void>> : public std::true_type {}; + +template +inline constexpr bool range_v = detray::ranges::range::value; + +template +inline constexpr bool input_range_v = detray::ranges::range_vand + std::is_base_of_v>::iterator_category>; + +template +inline constexpr bool output_range_v = detray::ranges::range_vand + std::is_base_of_v>::iterator_category>; + +template +inline constexpr bool forward_range_v = detray::ranges::range_vand + std::is_base_of_v>::iterator_category>; + +template +inline constexpr bool bidirectional_range_v = detray::ranges::range_vand + std::is_base_of_v>::iterator_category>; + +template +inline constexpr bool random_access_range_v = detray::ranges::range_vand + std::is_base_of_v>::iterator_category>; + +// Available only in c++20 +/*template +inline constexpr bool contiguous_range_v = range_v and +std::is_base_of_v>::iterator_category>;*/ + +/// @see https://en.cppreference.com/w/cpp/ranges/borrowed_range +template +inline constexpr bool enable_borrowed_range = false; + +template +inline constexpr bool borrowed_range = + detray::ranges::range_v && + (std::is_lvalue_reference_v || + ranges::enable_borrowed_range< + std::remove_reference_t>>); + +/// @brief models a dangling iterator +/// @see https://en.cppreference.com/w/cpp/ranges/dangling +struct dangling { + constexpr dangling() noexcept = default; + template + constexpr dangling(Args&&...) noexcept {} +}; + +template +using borrowed_iterator_t = + std::conditional_t, detray::ranges::iterator_t, + dangling>; + +/// @see https://en.cppreference.com/w/cpp/ranges/common_range +template +inline constexpr bool common_range = + detray::ranges::range_v&& std::is_same_v, + detray::ranges::sentinel_t>; +/// @} + +/// Definition of 'view' +/// @see https://en.cppreference.com/w/cpp/ranges/view +/// @{ /// Tags a type as a view struct base_view {}; @@ -150,4 +228,15 @@ struct view_interface : public base_view { view_impl_t* const m_impl_ptr{static_cast(this)}; }; +/// View traits +/// @{ +template +inline constexpr bool enable_view = + std::is_base_of_v or std::is_base_of_v, R>; + +template +inline constexpr bool view = detray::ranges::range_vand std::is_object_v&& + std::is_move_constructible_vand enable_view; +/// @} + } // namespace detray::ranges \ No newline at end of file diff --git a/core/include/detray/utils/ranges/subrange.hpp b/core/include/detray/utils/ranges/subrange.hpp index 89b38da1ad..9534b4a783 100644 --- a/core/include/detray/utils/ranges/subrange.hpp +++ b/core/include/detray/utils/ranges/subrange.hpp @@ -131,4 +131,10 @@ template subrange; +/// @see https://en.cppreference.com/w/cpp/ranges/borrowed_iterator_t +template +using borrowed_subrange_t = + std::conditional_t, detray::ranges::subrange, + dangling>; + } // namespace detray::ranges diff --git a/tests/unit_tests/core/utils_ranges.cpp b/tests/unit_tests/core/utils_ranges.cpp index a296b18ac2..0839acceb6 100644 --- a/tests/unit_tests/core/utils_ranges.cpp +++ b/tests/unit_tests/core/utils_ranges.cpp @@ -11,13 +11,84 @@ #include "tests/common/test_defs.hpp" // detray core +#include "detray/definitions/containers.hpp" #include "detray/utils/ranges.hpp" +// Vecmem include(s) +#include "vecmem/containers/device_vector.hpp" + // System include(s) +#include +#include #include using namespace detray; +// Test basic ranges definitions +TEST(utils, ranges) { + // + // detray containers + // + // std::vector + static_assert(detray::ranges::range_v>); + static_assert(not detray::ranges::view>); + static_assert(detray::ranges::random_access_range_v>); + static_assert(detray::ranges::common_range>); + + // std::array + static_assert(detray::ranges::range_v>); + static_assert(not detray::ranges::view>); + static_assert(detray::ranges::random_access_range_v>); + static_assert(detray::ranges::common_range>); + + // std::map + static_assert(detray::ranges::range_v>); + static_assert(not detray::ranges::view>); + static_assert(detray::ranges::bidirectional_range_v>); + static_assert(not detray::ranges::random_access_range_v>); + static_assert(detray::ranges::common_range>); + + // std::tuple + static_assert(not detray::ranges::range_v>); + static_assert(not detray::ranges::view>); + + // + // vecmem containers + // + + // vecmem::device_vector + static_assert(detray::ranges::range_v>); + static_assert(not detray::ranges::view>); + static_assert( + detray::ranges::random_access_range_v>); + static_assert(detray::ranges::common_range>); + + // vecmem::jagged_vector + static_assert(detray::ranges::range_v>); + static_assert(not detray::ranges::view>); + static_assert(detray::ranges::random_access_range_v>); + static_assert(detray::ranges::common_range>); + + // + // Some additional STL containers + // + + // std::forward_list + static_assert(detray::ranges::range_v>); + static_assert(not detray::ranges::view>); + static_assert(detray::ranges::forward_range_v>); + static_assert( + not detray::ranges::bidirectional_range_v>); + static_assert(detray::ranges::common_range>); + + // std::list + static_assert(detray::ranges::range_v>); + static_assert(not detray::ranges::view>); + static_assert(detray::ranges::bidirectional_range_v>); + static_assert(not detray::ranges::random_access_range_v>); + static_assert(detray::ranges::common_range>); +} + // Unittest for the generation of a single element sequence TEST(utils, ranges_single) { @@ -26,21 +97,18 @@ TEST(utils, ranges_single) { auto sngl = detray::views::single(value); // general tests - ASSERT_TRUE(detray::ranges::range::value); - ASSERT_TRUE(detray::ranges::is_view); - ASSERT_TRUE(std::is_copy_assignable_v); + static_assert(std::is_copy_assignable_v); + static_assert(detray::ranges::range_v); + static_assert(detray::ranges::view); + static_assert(detray::ranges::bidirectional_range_v); + static_assert(not detray::ranges::random_access_range_v); // Test prerequisits for LagacyIterator - ASSERT_TRUE( + static_assert( std::is_copy_constructible_v); - ASSERT_TRUE(std::is_copy_assignable_v); - ASSERT_TRUE(std::is_destructible_v); - - // Check iterator category - constexpr bool is_correct_itr_tag = - std::is_same_v; - ASSERT_TRUE(is_correct_itr_tag); + static_assert( + std::is_copy_assignable_v); + static_assert(std::is_destructible_v); // Test inherited member functions ASSERT_EQ(sngl[0], value); @@ -62,21 +130,18 @@ TEST(utils, ranges_iota_single) { auto seq = detray::views::iota(single); // general tests - ASSERT_TRUE(detray::ranges::range::value); - ASSERT_TRUE(detray::ranges::is_view); - ASSERT_TRUE(std::is_copy_assignable_v); + static_assert(std::is_copy_assignable_v); + static_assert(detray::ranges::range_v); + static_assert(detray::ranges::view); + static_assert(detray::ranges::input_range_v); + static_assert(not detray::ranges::forward_range_v); // Test prerequisits for LagacyIterator - ASSERT_TRUE( + static_assert( std::is_copy_constructible_v); - ASSERT_TRUE(std::is_copy_assignable_v); - ASSERT_TRUE(std::is_destructible_v); - - // Check iterator category - constexpr bool is_correct_itr_tag = - std::is_same_v; - ASSERT_TRUE(is_correct_itr_tag); + static_assert( + std::is_copy_assignable_v); + static_assert(std::is_destructible_v); // Test inherited member functions ASSERT_EQ(seq[1], single + 1); @@ -98,21 +163,18 @@ TEST(utils, ranges_iota_interval) { auto seq = detray::views::iota(interval); // general tests - ASSERT_TRUE(detray::ranges::range::value); - ASSERT_TRUE(detray::ranges::is_view); - ASSERT_TRUE(std::is_copy_assignable_v); + static_assert(detray::ranges::range_v); + static_assert(detray::ranges::view); + static_assert(std::is_copy_assignable_v); + static_assert(detray::ranges::input_range_v); + static_assert(not detray::ranges::forward_range_v); // Test prerequisits for LagacyIterator - ASSERT_TRUE( + static_assert( std::is_copy_constructible_v); - ASSERT_TRUE(std::is_copy_assignable_v); - ASSERT_TRUE(std::is_destructible_v); - - // Check iterator category - constexpr bool is_correct_itr_tag = - std::is_same_v; - ASSERT_TRUE(is_correct_itr_tag); + static_assert( + std::is_copy_assignable_v); + static_assert(std::is_destructible_v); // Test inherited member functions ASSERT_EQ(seq[1], 3UL); @@ -141,24 +203,19 @@ TEST(utils, ranges_enumerate) { auto enumerator = detray::views::enumerate(seq); // general tests - ASSERT_TRUE(detray::ranges::range::value); - ASSERT_TRUE(detray::ranges::is_view); - ASSERT_TRUE(std::is_copy_assignable_v); + static_assert(detray::ranges::range_v); + static_assert(detray::ranges::view); + static_assert(std::is_copy_assignable_v); + static_assert(detray::ranges::random_access_range_v); // Test prerequisits for LagacyIterator - ASSERT_TRUE(std::is_copy_constructible_v< - typename decltype(enumerator)::iterator_t>); - ASSERT_TRUE( + static_assert(std::is_copy_constructible_v< + typename decltype(enumerator)::iterator_t>); + static_assert( std::is_copy_assignable_v); - ASSERT_TRUE( + static_assert( std::is_destructible_v); - // Check iterator category - constexpr bool is_correct_itr_tag = std::is_same_v< - typename decltype(enumerator)::iterator_t::iterator_category, - std::random_access_iterator_tag>; - ASSERT_TRUE(is_correct_itr_tag); - // Test inherited member functions const auto [i, v] = enumerator[2]; ASSERT_EQ(i, 2UL); @@ -189,22 +246,18 @@ TEST(utils, ranges_chain) { auto chained = detray::views::chain(interval_1, interval_2); // general tests - ASSERT_TRUE(detray::ranges::range::value); - ASSERT_TRUE(detray::ranges::is_view); - ASSERT_TRUE(std::is_copy_assignable_v); + static_assert(detray::ranges::range_v); + static_assert(detray::ranges::view); + static_assert(std::is_copy_assignable_v); + static_assert(detray::ranges::random_access_range_v); // Test prerequisits for LagacyIterator - ASSERT_TRUE( + static_assert( std::is_copy_constructible_v); - ASSERT_TRUE( + static_assert( std::is_copy_assignable_v); - ASSERT_TRUE(std::is_destructible_v); - - // Check iterator category - constexpr bool is_correct_itr_tag = std::is_same_v< - typename decltype(chained)::iterator_t::iterator_category, - std::random_access_iterator_tag>; - ASSERT_TRUE(is_correct_itr_tag); + static_assert( + std::is_destructible_v); // Test inherited member functions ASSERT_EQ(chained[1], 3UL); @@ -236,24 +289,19 @@ TEST(utils, ranges_pick) { auto selected = detray::views::pick(seq, indices); // general tests - ASSERT_TRUE(detray::ranges::range::value); - ASSERT_TRUE(detray::ranges::is_view); - ASSERT_TRUE(std::is_copy_assignable_v); + static_assert(detray::ranges::range_v); + static_assert(detray::ranges::view); + static_assert(std::is_copy_assignable_v); + static_assert(detray::ranges::random_access_range_v); // Test prerequisits for LagacyIterator - ASSERT_TRUE( + static_assert( std::is_copy_constructible_v); - ASSERT_TRUE( + static_assert( std::is_copy_assignable_v); - ASSERT_TRUE( + static_assert( std::is_destructible_v); - // Check iterator category - constexpr bool is_correct_itr_tag = std::is_same_v< - typename decltype(selected)::iterator_t::iterator_category, - std::random_access_iterator_tag>; - ASSERT_TRUE(is_correct_itr_tag); - // Test inherited member functions const auto [i, v] = selected[2]; ASSERT_EQ(i, 7UL); @@ -286,21 +334,16 @@ TEST(utils, ranges_subrange) { auto sr = detray::ranges::subrange(seq, interval); // general tests - ASSERT_TRUE(detray::ranges::range::value); - ASSERT_TRUE(detray::ranges::is_view); - ASSERT_TRUE(std::is_copy_assignable_v); + static_assert(detray::ranges::range_v); + static_assert(detray::ranges::view); + static_assert(std::is_copy_assignable_v); + static_assert(detray::ranges::random_access_range_v); // Test prerequisits for LagacyIterator - ASSERT_TRUE( + static_assert( std::is_copy_constructible_v); - ASSERT_TRUE(std::is_copy_assignable_v); - ASSERT_TRUE(std::is_destructible_v); - - // Check iterator category - constexpr bool is_correct_itr_tag = - std::is_same_v; - ASSERT_TRUE(is_correct_itr_tag); + static_assert(std::is_copy_assignable_v); + static_assert(std::is_destructible_v); ASSERT_EQ(sr[1], seq[begin + 1]); ASSERT_EQ(sr.size(), 3UL); @@ -336,10 +379,8 @@ TEST(utils, ranges_subrange_iota) { auto iota_sr = detray::ranges::subrange(detray::views::iota(seq), interval); // Check iterator category - constexpr bool is_correct_itr_tag = std::is_same_v< - typename decltype(iota_sr)::iterator_t::iterator_category, - std::input_iterator_tag>; - ASSERT_TRUE(is_correct_itr_tag); + static_assert(detray::ranges::input_range_v); + static_assert(not detray::ranges::forward_range_v); std::size_t i{4}; for (const auto v : iota_sr) { @@ -364,10 +405,7 @@ TEST(utils, ranges_enumerated_subrange) { detray::views::enumerate(detray::ranges::subrange(seq, interval)); // Check iterator category - constexpr bool is_correct_itr_tag = std::is_same_v< - typename decltype(enum_sr)::iterator_t::iterator_category, - std::random_access_iterator_tag>; - ASSERT_TRUE(is_correct_itr_tag); + static_assert(detray::ranges::random_access_range_v); for (const auto [i, v] : enum_sr) { ASSERT_EQ(i, v.ui - 1); @@ -395,14 +433,10 @@ TEST(utils, ranges_pick_chained_sequence) { auto selected = detray::views::pick(seq, indices); // Check iterator category - bool is_correct_itr_tag = std::is_same_v< - typename decltype(indices)::iterator_t::iterator_category, - std::input_iterator_tag>; - ASSERT_TRUE(is_correct_itr_tag); - is_correct_itr_tag = std::is_same_v< - typename decltype(selected)::iterator_t::iterator_category, - std::input_iterator_tag>; - ASSERT_TRUE(is_correct_itr_tag); + static_assert(detray::ranges::input_range_v); + static_assert(not detray::ranges::forward_range_v); + static_assert(detray::ranges::input_range_v); + static_assert(not detray::ranges::forward_range_v); // Test inherited member functions const auto [i, v] = selected[2];