Skip to content

Commit 630c497

Browse files
Add support for views without a subscription operator (#2493)
1 parent db48f7f commit 630c497

File tree

4 files changed

+446
-156
lines changed

4 files changed

+446
-156
lines changed

include/oneapi/dpl/pstl/utils_ranges.h

Lines changed: 158 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <utility> // std::declval
2323
#include <iterator> // std::iterator_traits
2424
#include <type_traits> // std::decay_t, std::remove_cv_t, std::remove_reference_t, std::invoke_result_t, ...
25+
#include <cassert> // assert
2526

2627
#if _ONEDPL_CPP20_RANGES_PRESENT
2728
# include <ranges> // std::ranges::sized_range, std::ranges::range_size_t
@@ -34,6 +35,107 @@ namespace oneapi
3435
{
3536
namespace dpl
3637
{
38+
namespace __ranges
39+
{
40+
#if _ONEDPL_CPP20_RANGES_PRESENT
41+
template <typename _Range>
42+
auto
43+
__begin(_Range&& __rng) -> decltype(std::ranges::begin(std::forward<_Range>(__rng)))
44+
{
45+
return std::ranges::begin(std::forward<_Range>(__rng));
46+
}
47+
#else
48+
template <typename _Range>
49+
auto
50+
__begin(_Range&& __rng) -> decltype(__rng.begin())
51+
{
52+
return __rng.begin();
53+
}
54+
#endif
55+
56+
#if _ONEDPL_CPP20_RANGES_PRESENT
57+
template <typename _Range>
58+
auto
59+
__end(_Range&& __rng) -> decltype(std::ranges::end(std::forward<_Range>(__rng)))
60+
{
61+
return std::ranges::end(std::forward<_Range>(__rng));
62+
}
63+
#else
64+
template <typename _Range>
65+
auto
66+
__end(_Range&& __rng) -> decltype(__rng.end())
67+
{
68+
return __rng.end();
69+
}
70+
#endif
71+
72+
#if _ONEDPL_CPP20_RANGES_PRESENT
73+
template <typename _Range>
74+
auto
75+
__size(_Range&& __rng) -> decltype(std::ranges::size(std::forward<_Range>(__rng)))
76+
{
77+
return std::ranges::size(std::forward<_Range>(__rng));
78+
}
79+
#else
80+
template <typename _R, typename = void>
81+
struct __has_size : std::false_type
82+
{
83+
};
84+
85+
template <typename _R>
86+
struct __has_size<_R, std::void_t<decltype(std::declval<_R>().size())>> : std::true_type
87+
{
88+
};
89+
90+
template <typename _Range>
91+
std::enable_if_t<__has_size<_Range>::value, decltype(std::declval<_Range>().size())>
92+
__size(_Range&& __rng)
93+
{
94+
return __rng.size();
95+
}
96+
97+
template <typename _Range>
98+
std::enable_if_t<!__has_size<_Range>::value, decltype(__end(std::declval<_Range>()) - __begin(std::declval<_Range>()))>
99+
__size(_Range&& __rng)
100+
{
101+
return __end(__rng) - __begin(__rng);
102+
}
103+
#endif
104+
105+
#if _ONEDPL_CPP20_RANGES_PRESENT
106+
template <typename _Range>
107+
bool
108+
__empty(_Range&& __rng)
109+
{
110+
return std::ranges::empty(__rng);
111+
}
112+
#else
113+
template <typename _R, typename = void>
114+
struct __has_empty : std::false_type
115+
{
116+
};
117+
118+
template <typename _R>
119+
struct __has_empty<_R, std::void_t<decltype(std::declval<_R>().empty())>> : std::true_type
120+
{
121+
};
122+
123+
template <typename _Range>
124+
std::enable_if_t<__has_empty<_Range>::value, bool>
125+
__empty(_Range&& __rng)
126+
{
127+
return __rng.empty();
128+
}
129+
130+
template <typename _Range>
131+
std::enable_if_t<!__has_empty<_Range>::value, bool>
132+
__empty(_Range&& __rng)
133+
{
134+
return __size(__rng) == 0;
135+
}
136+
#endif
137+
138+
} // namespace __ranges
37139

38140
namespace __internal
39141
{
@@ -44,8 +146,8 @@ get_value_type(int) -> typename ::std::decay_t<_R>::value_type;
44146

45147
template <typename _R>
46148
auto
47-
get_value_type(long) ->
48-
typename ::std::iterator_traits<::std::decay_t<decltype(::std::declval<_R&>().begin())>>::value_type;
149+
get_value_type(long) -> typename std::iterator_traits<
150+
std::decay_t<decltype(oneapi::dpl::__ranges::__begin(std::declval<_R&>()))>>::value_type;
49151

50152
template <typename _It>
51153
auto
@@ -87,21 +189,28 @@ using __range_size_t = typename __range_size<_R>::type;
87189

88190
template <typename _R>
89191
auto
90-
__check_size(int) -> decltype(std::declval<_R&>().size());
192+
__check_size(int) -> decltype(__get_buffer_size(std::declval<_R&>()));
91193

92194
template <typename _R>
93195
auto
94-
__check_size(long) -> decltype(std::declval<_R&>().get_count());
196+
__check_size(long) -> decltype(oneapi::dpl::__ranges::__size(std::declval<_R&>()));
95197

96-
#if _ONEDPL_CPP20_RANGES_PRESENT
97-
template <typename _R>
198+
template <typename _It>
98199
auto
99-
__check_size(long long) -> decltype(std::ranges::size(std::declval<_R&>()));
100-
#endif // _ONEDPL_CPP20_RANGES_PRESENT
200+
__check_size(long long) -> typename std::iterator_traits<_It>::difference_type;
101201

102-
template <typename _It>
202+
template <typename _R>
103203
auto
104-
__check_size(...) -> typename std::iterator_traits<_It>::difference_type;
204+
__check_size(...)
205+
{
206+
//static_assert should always fail when this overload is chosen, so its condition must depend on
207+
//the template parameter and evaluate to false
208+
static_assert(
209+
std::is_same_v<_R, void>,
210+
"error: unable to determine the size of the range; the range must provide a 'get_count()' or 'size()' member, "
211+
"or otherwise support size determination (e.g., via std::ranges::size or by supporting begin/end for distance "
212+
"calculation)");
213+
}
105214

106215
template <typename _R>
107216
using __difference_t = std::make_signed_t<decltype(__check_size<_R>(0))>;
@@ -121,70 +230,6 @@ using projected_value_t = std::remove_cvref_t<std::invoke_result_t<Proj&, std::i
121230
namespace __ranges
122231
{
123232

124-
#if _ONEDPL_CPP20_RANGES_PRESENT
125-
template <typename _Range>
126-
bool
127-
__empty(_Range&& __rng)
128-
{
129-
return std::ranges::empty(__rng);
130-
}
131-
#else
132-
template <typename _R, typename = void>
133-
struct __has_empty : std::false_type
134-
{
135-
};
136-
137-
template <typename _R>
138-
struct __has_empty<_R, std::void_t<decltype(std::declval<_R>().empty())>> : std::true_type
139-
{
140-
};
141-
142-
template <typename _Range>
143-
bool
144-
__empty(_Range&& __rng)
145-
{
146-
if constexpr (__has_empty<_Range>::value)
147-
return __rng.empty();
148-
else
149-
return __rng.begin() == __rng.end();
150-
}
151-
#endif
152-
153-
template <typename _R, typename = void>
154-
struct __has_size : std::false_type
155-
{
156-
};
157-
158-
template <typename _R>
159-
struct __has_size<_R, std::void_t<decltype(std::declval<_R>().size())>> : std::true_type
160-
{
161-
};
162-
163-
template <typename _Range>
164-
std::enable_if_t<__has_size<_Range>::value, decltype(std::declval<_Range>().size())>
165-
__size(_Range&& __rng)
166-
{
167-
return __rng.size();
168-
}
169-
170-
#if _ONEDPL_CPP20_RANGES_PRESENT
171-
template <typename _Range>
172-
std::enable_if_t<!__has_size<_Range>::value,
173-
decltype(std::ranges::distance(std::declval<_Range>().begin(), std::declval<_Range>().end()))>
174-
__size(_Range&& __rng)
175-
{
176-
return std::ranges::distance(__rng.begin(), __rng.end());
177-
}
178-
#else
179-
template <typename _Range>
180-
std::enable_if_t<!__has_size<_Range>::value,
181-
decltype(std::distance(std::declval<_Range>().begin(), std::declval<_Range>().end()))>
182-
__size(_Range&& __rng)
183-
{
184-
return std::distance(__rng.begin(), __rng.end());
185-
}
186-
#endif
187-
188233
template <typename... _Rng>
189234
using __common_size_t = std::common_type_t<std::make_unsigned_t<decltype(__size(std::declval<_Rng>()))>...>;
190235

@@ -725,58 +770,62 @@ struct __has_subscription_op<_R, std::void_t<decltype(std::declval<_R>().operato
725770
{
726771
};
727772

728-
template <typename _Source, typename _Base = std::decay_t<_Source>>
729-
struct __subscription_impl_view_simple : _Base
773+
template <typename _Range, typename = std::enable_if_t<__has_subscription_op<_Range>::value>>
774+
_Range&&
775+
__get_subscription_view(_Range&& __rng)
730776
{
731-
static_assert(
732-
!__has_subscription_op<_Base>::value,
733-
"The usage of __subscription_impl_view_simple prohibited if std::decay_t<_Source>::operator[] implemented");
777+
// If the range supports operator[], return it as is
778+
return std::forward<_Range>(__rng);
779+
}
734780

735-
using value_type = oneapi::dpl::__internal::__value_t<_Base>;
736-
using index_type = oneapi::dpl::__internal::__difference_t<_Base>;
781+
#if _ONEDPL_CPP20_RANGES_PRESENT
782+
template <std::ranges::view _View>
783+
requires std::ranges::random_access_range<_View>
784+
struct __subscription_impl_view_simple : std::ranges::view_interface<__subscription_impl_view_simple<_View>>
785+
{
786+
static_assert(!__has_subscription_op<_View>::value,
787+
"The usage of __subscription_impl_view_simple prohibited if _View::operator[] implemented");
788+
789+
_View __base;
737790

738-
// Define default constructors
739-
__subscription_impl_view_simple(const __subscription_impl_view_simple&) = default;
740-
__subscription_impl_view_simple(__subscription_impl_view_simple&&) = default;
791+
constexpr explicit __subscription_impl_view_simple(_View __view) : __base(std::move(__view)) {}
741792

742-
// Define custom constructor to forward arguments to the base class
743-
template <typename... _Args>
744-
__subscription_impl_view_simple(_Args&&... __args) : _Base(std::forward<_Args>(__args)...)
793+
constexpr auto
794+
begin() const
745795
{
796+
return __begin(__base);
746797
}
747798

748-
// Define default operator=
749-
__subscription_impl_view_simple&
750-
operator=(const __subscription_impl_view_simple&) = default;
751-
__subscription_impl_view_simple&
752-
operator=(__subscription_impl_view_simple&&) = default;
799+
constexpr auto
800+
end() const
801+
{
802+
return __end(__base);
803+
}
753804

754-
decltype(auto)
755-
operator[](index_type __i)
805+
constexpr auto
806+
size() const
756807
{
757-
return *std::next(_Base::begin(), __i);
808+
return __size(__base);
758809
}
759810

760-
decltype(auto)
761-
operator[](index_type __i) const
811+
constexpr _View
812+
base() const
762813
{
763-
return *std::next(_Base::begin(), __i);
814+
return __base;
764815
}
765816
};
766817

767-
template <typename _Range>
768-
decltype(auto)
769-
__get_subscription_view(_Range&& __rng)
818+
template <typename _View, typename _ViewInstance = std::remove_cvref_t<_View>>
819+
requires(!__has_subscription_op<_ViewInstance>::value) && std::ranges::view<_ViewInstance> &&
820+
std::ranges::random_access_range<_ViewInstance>
821+
auto
822+
__get_subscription_view(_View&& __view)
770823
{
771-
if constexpr (__has_subscription_op<_Range>::value)
772-
{
773-
return std::forward<_Range>(__rng);
774-
}
775-
else
776-
{
777-
return __subscription_impl_view_simple<_Range>(std::forward<_Range>(__rng));
778-
}
824+
// If the view doesn't support operator[], wrap it with __subscription_impl_view_simple
825+
// to provide operator[] access and extend lifetime if necessary (for temporary ranges).
826+
return __subscription_impl_view_simple<_ViewInstance>(__view);
779827
}
828+
#endif // _ONEDPL_CPP20_RANGES_PRESENT
780829

781830
} // namespace __ranges
782831
} // namespace dpl

0 commit comments

Comments
 (0)