Skip to content

Commit 7a4c26d

Browse files
committed
[feature] radr::elements, radr::keys, radr::values
1 parent ea82617 commit 7a4c26d

File tree

9 files changed

+369
-69
lines changed

9 files changed

+369
-69
lines changed

docs/feature_table.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@
88
| `radr::as_rvalue` | | input | input/ra | = | = | returns only input ranges in C++20 |
99
| `radr::drop(n)` | !(ra+sized) | input | contig | = || |
1010
| `radr::drop_while(fn)` | always | input | contig ||| |
11+
| `radr::elements<I>` | | input | ra | = | = | |
1112
| `radr::filter(fn)` | always | input | bidi | - || |
1213
| `radr::join` | | input | (bidi) | - | = | less strict than std::views::join |
14+
| `radr::keys` | | input | ra | = | = | |
1315
| `radr::reverse` | non-common | bidi | ra | = | + | |
1416
| `radr::slice(m, n)` | !(ra+sized) | input | contig | = | = | get subrange between m and n |
1517
| `radr::split(pat)` | always | input | fwd | - || |
@@ -19,6 +21,7 @@
1921
| `radr::to_common` | !(common) | fwd | contig || + | |
2022
| `radr::to_single_pass` | | input | input | - | - | demotes range category to single-pass |
2123
| `radr::transform(fn)` | | input | ra | = | = | |
24+
| `radr::values` | | input | ra | = | = | |
2225

2326
**min cat** underlying range required to be at least input (`input_range`), fwd (`forward_range`), bidi (`bidirectional_range`),
2427
ra (`random_access_range`) or contig (`contiguous_range`)<br>

docs/implementation_status.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@ We plan to add equivalent objects for all standard library adaptors.
2626
| `radr::as_rvalue` | C++20 | | `std::views::as_rvalue` | **C++23** | *returns only input ranges in C++20 |
2727
| `radr::drop(n)` | C++20 | | `std::views::drop` | C++20 | |
2828
| `radr::drop_while(fn)` | C++20 | | `std::views::drop_while` | C++20 | |
29+
| `radr::elements<I>` | C++20 | | `std::views::elements` | C++20 | |
2930
| `radr::filter(fn)` | C++20 | | `std::views::filter` | C++20 | |
3031
| `radr::join` | C++20 | | `std::views::join` | C++20 | |
32+
| `radr::keys` | C++20 | | `std::views::keys` | C++20 | |
3133
| `radr::reverse` | C++20 | | `std::views::reverse` | C++20 | |
3234
| `radr::slice(m, n)` | C++20 | | *not yet available* | | get subrange between m and n |
3335
| `radr::split(pat)` | C++20 | | `std::views::split` | C++20 | |
@@ -37,6 +39,8 @@ We plan to add equivalent objects for all standard library adaptors.
3739
| `radr::to_common` | C++20 | | `std::views::common`[^diff] | C++20 | turns non-common into common |
3840
| `radr::to_single_pass` | C++20 | | `std::views::to_input`[^diff] | **C++26** | demotes range category to input |
3941
| `radr::transform(fn)` | C++20 | | `std::views::transform` | C++20 | |
42+
| `radr::values` | C++20 | | `std::views::values` | C++20 | |
43+
4044

4145
All range adaptors from this library are available in C++20, although `radr::as_rvalue` behaves slightly different between modes.
4246

include/radr/detail/detail.hpp

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -168,16 +168,20 @@ using iterator_tag_t = std::conditional_t<std::contiguous_iterator<It>, std:
168168
// tuple_like
169169
//=============================================================================
170170

171-
template <class _Tp>
171+
template <class Tp>
172172
struct tuple_like_impl : std::false_type
173173
{};
174174

175-
template <class _T1, class _T2>
176-
struct tuple_like_impl<std::pair<_T1, _T2>> : std::true_type
175+
template <class... Args>
176+
struct tuple_like_impl<std::tuple<Args...>> : std::true_type
177+
{};
178+
179+
template <class T1, class T2>
180+
struct tuple_like_impl<std::pair<T1, T2>> : std::true_type
177181
{};
178182

179-
template <class _Tp, size_t _Size>
180-
struct tuple_like_impl<std::array<_Tp, _Size>> : std::true_type
183+
template <class Tp, size_t _Size>
184+
struct tuple_like_impl<std::array<Tp, _Size>> : std::true_type
181185
{};
182186

183187
template <class _Ip, class _Sp, std::ranges::subrange_kind _Kp>
@@ -188,11 +192,11 @@ template <class _Ip, class _Sp, class _CIp, class _CSp, borrowing_rad_kind _Kp>
188192
struct tuple_like_impl<borrowing_rad<_Ip, _Sp, _CIp, _CSp, _Kp>> : std::true_type
189193
{};
190194

191-
template <class _Tp>
192-
concept tuple_like = tuple_like_impl<std::remove_cvref_t<_Tp>>::value;
195+
template <class Tp>
196+
concept tuple_like = tuple_like_impl<std::remove_cvref_t<Tp>>::value;
193197

194-
template <class _Tp>
195-
concept pair_like = tuple_like<_Tp> && std::tuple_size<std::remove_cvref_t<_Tp>>::value == 2;
198+
template <class Tp>
199+
concept pair_like = tuple_like<Tp> && std::tuple_size<std::remove_cvref_t<Tp>>::value == 2;
196200

197201
//=============================================================================
198202
// add_const_t

include/radr/rad/elements.hpp

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// -*- C++ -*-
2+
//===----------------------------------------------------------------------===//
3+
//
4+
// Copyright (c) 2023-2025 Hannes Hauswedell
5+
//
6+
// Licensed under the Apache License v2.0 with LLVM Exceptions.
7+
// See the LICENSE file for details.
8+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
9+
//
10+
//===----------------------------------------------------------------------===//
11+
12+
#pragma once
13+
14+
#include <functional>
15+
#include <ranges>
16+
17+
#include "../detail/detail.hpp"
18+
#include "transform.hpp"
19+
20+
namespace radr::detail
21+
{
22+
23+
template <size_t I>
24+
struct elements_fn
25+
{
26+
template <typename Tuple>
27+
constexpr decltype(auto) operator()(Tuple && tuple) const
28+
{
29+
static_assert(tuple_like<Tuple>, "The element type of the underlying range must be a tuple or pair.");
30+
static_assert(std::tuple_size_v<std::remove_reference_t<Tuple>> > I,
31+
"The element number requested by radr::elements was larger than the tuple-size.");
32+
33+
if constexpr (std::is_reference_v<Tuple>)
34+
{
35+
return std::get<I>(tuple);
36+
}
37+
else
38+
{
39+
using E = std::remove_cv_t<std::tuple_element_t<I, Tuple>>;
40+
return static_cast<E>(std::get<I>(tuple));
41+
}
42+
}
43+
};
44+
45+
} // namespace radr::detail
46+
47+
namespace radr
48+
{
49+
50+
inline namespace cpo
51+
{
52+
/*!\brief Projects a range of tuples to the I-th element of the tuple.
53+
* \param urange The underlying range.
54+
* \tparam URange Type of \p.
55+
* \tparam I The index of the element to be projected to.
56+
*
57+
* ### Multi-pass adaptor
58+
*
59+
* Requirements:
60+
* * `radr::mp_range<URange>`
61+
* * `radr::detail::tuple_like<std::ranges::range_reference_t<URange>>`
62+
*
63+
* This adaptor preserves:
64+
* * categories up to std::ranges::random_access_range
65+
* * std::ranges::borrowed_range
66+
* * std::ranges::sized_range
67+
* * radr::common_range
68+
* * radr::constant_range
69+
* * radr::mutable_range
70+
*
71+
* ### Single-pass adaptor
72+
*
73+
* Requirements:
74+
* * `std::ranges::input_range<URange>`
75+
*/
76+
77+
template <size_t I>
78+
inline constexpr auto elements = radr::transform(detail::elements_fn<I>{});
79+
80+
/*!\brief Projects a range of tuples to the 0th element of the tuple.
81+
* \param urange The underlying range.
82+
* \tparam URange Type of \p.
83+
*
84+
* See radr::elements.
85+
*/
86+
inline constexpr auto keys = elements<0>;
87+
88+
/*!\brief Projects a range of tuples to the 1st element of the tuple.
89+
* \param urange The underlying range.
90+
* \tparam URange Type of \p.
91+
*
92+
* See radr::elements.
93+
*/
94+
inline constexpr auto values = elements<1>;
95+
} // namespace cpo
96+
} // namespace radr

include/radr/rad/to_single_pass.hpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,17 @@ namespace radr::detail
2424
inline constexpr auto to_single_pass_coro = detail::overloaded{
2525
[]<std::ranges::forward_range URange>(URange && urange)
2626
{
27-
static_assert(!std::is_lvalue_reference_v<URange>, RADR_ASSERTSTRING_RVALUE);
28-
static_assert(std::movable<URange>, RADR_ASSERTSTRING_MOVABLE);
27+
static_assert((!std::is_lvalue_reference_v<URange> && std::movable<URange>) ||
28+
(std::ranges::borrowed_range<std::remove_reference_t<URange>> &&
29+
std::copyable<std::remove_reference_t<URange>>),
30+
"You may pass any movable rvalue-to-range or copyable lvalues-to-borrowed_range to "
31+
"radr::single_pass.");
2932

3033
return
3134
[](auto urange_) -> radr::generator<std::ranges::range_reference_t<URange>, std::ranges::range_value_t<URange>>
3235
{
3336
co_yield elements_of(urange_);
34-
}(std::move(urange));
37+
}(std::forward<URange>(urange));
3538
},
3639
[]<std::ranges::input_range URange>(URange && urange) -> decltype(auto) // forward single-pass ranges as-is
3740
{

include/radr/rad/transform.hpp

Lines changed: 69 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#pragma once
1414

15+
#include <algorithm>
1516
#include <concepts>
1617
#include <functional>
1718
#include <iterator>
@@ -42,8 +43,7 @@ struct nest_fn
4243
};
4344

4445
template <class Iter, class Fn>
45-
concept fn_constraints = std::is_object_v<Fn> && std::copy_constructible<Fn> &&
46-
std::regular_invocable<Fn const &, std::iter_reference_t<Iter>> &&
46+
concept fn_constraints = std::is_object_v<Fn> && std::regular_invocable<Fn const &, std::iter_reference_t<Iter>> &&
4747
can_reference<std::invoke_result_t<Fn const &, std::iter_reference_t<Iter>>>;
4848

4949
} // namespace radr::detail::transform
@@ -280,85 +280,98 @@ class transform_sentinel
280280
}
281281
};
282282

283-
inline constexpr auto transform_borrow_impl =
284-
[]<typename UIt, typename USen, typename UCIt, typename UCSen, typename Fn, typename Size>(UIt it,
285-
USen sen,
286-
UCIt,
287-
UCSen,
288-
Size size,
289-
Fn fn)
283+
inline constexpr auto transform_borrow = []<typename URange, typename Fn>(URange && urange, Fn fn)
290284
{
291-
using iterator_t = transform_iterator<UIt, Fn>;
292-
using sentinel_t = std::conditional_t<std::same_as<UIt, USen>, iterator_t, transform_sentinel<UIt, USen, Fn>>;
293-
294-
using const_iterator_t = transform_iterator<UCIt, Fn>;
295-
using const_sentinel_t =
296-
std::conditional_t<std::same_as<UCIt, UCSen>, const_iterator_t, transform_sentinel<UCIt, UCSen, Fn>>;
297-
298-
static constexpr auto kind = decays_to<Size, not_size> ? borrowing_rad_kind::unsized : borrowing_rad_kind::sized;
299-
300-
using BorrowingRad = borrowing_rad<iterator_t, sentinel_t, const_iterator_t, const_sentinel_t, kind>;
301-
return BorrowingRad{
302-
iterator_t{fn, it},
303-
sentinel_t{fn, sen},
304-
size
285+
static_assert(borrowed_mp_range<URange>, "The constraints for radr::transform's underlying range are not met.");
286+
static_assert(detail::transform::fn_constraints<radr::iterator_t<URange>, Fn> &&
287+
detail::transform::fn_constraints<radr::const_iterator_t<URange>, Fn>,
288+
"The constraints for radr::transform's functor are not met.");
289+
290+
constexpr auto impl =
291+
[]<typename UIt, typename USen, typename UCIt, typename UCSen, typename Fn_, typename Size>(UIt it,
292+
USen sen,
293+
UCIt,
294+
UCSen,
295+
Size size,
296+
Fn_ fn)
297+
{
298+
using iterator_t = transform_iterator<UIt, Fn_>;
299+
using sentinel_t = std::conditional_t<std::same_as<UIt, USen>, iterator_t, transform_sentinel<UIt, USen, Fn>>;
300+
301+
using const_iterator_t = transform_iterator<UCIt, Fn_>;
302+
using const_sentinel_t =
303+
std::conditional_t<std::same_as<UCIt, UCSen>, const_iterator_t, transform_sentinel<UCIt, UCSen, Fn_>>;
304+
305+
static constexpr auto kind =
306+
decays_to<Size, not_size> ? borrowing_rad_kind::unsized : borrowing_rad_kind::sized;
307+
308+
using BorrowingRad = borrowing_rad<iterator_t, sentinel_t, const_iterator_t, const_sentinel_t, kind>;
309+
return BorrowingRad{
310+
iterator_t{fn, it},
311+
sentinel_t{fn, sen},
312+
size
313+
};
305314
};
306-
};
307315

308-
inline constexpr auto transform_borrow =
309-
[]<borrowed_mp_range URange, std::copy_constructible Fn>(URange && urange, Fn fn)
310-
requires(detail::transform::fn_constraints<radr::iterator_t<URange>, Fn>)
311-
{
312-
// dispatch between generic case and chained case(s)
316+
/* dispatch between generic case and chained case(s) */
317+
// clang-format off
313318
return overloaded{
314-
transform_borrow_impl,
315-
[]<typename UIt, typename UCIt, typename UFn, typename Size, typename Fn_>(transform_iterator<UIt, UFn> iter,
316-
transform_iterator<UIt, UFn> sen,
317-
transform_iterator<UCIt, UFn> citer,
318-
transform_iterator<UCIt, UFn> csen,
319-
Size size,
320-
Fn_ new_fn)
319+
/* generic */
320+
impl,
321+
/* nested common */
322+
[]<typename UIt, typename UCIt, typename UFn, typename Size>(
323+
transform_iterator<UIt, UFn> iter,
324+
transform_iterator<UIt, UFn> sen,
325+
transform_iterator<UCIt, UFn> citer,
326+
transform_iterator<UCIt, UFn> csen,
327+
Size size,
328+
Fn new_fn)
321329
{
322-
return transform_borrow_impl(std::move(iter).base(),
323-
std::move(sen).base(),
324-
std::move(citer).base(),
325-
std::move(csen).base(),
326-
size,
327-
transform::nest_fn{std::move(iter).func(), std::move(new_fn)});
330+
return decltype(impl){}(std::move(iter).base(),
331+
std::move(sen).base(),
332+
std::move(citer).base(),
333+
std::move(csen).base(),
334+
size,
335+
transform::nest_fn{std::move(iter).func(), std::move(new_fn)});
328336
},
329-
[]<typename UIt, typename USen, typename UCIt, typename UCSen, typename UFn, typename Size, typename Fn_>(
337+
/* nested non-common */
338+
[]<typename UIt, typename USen, typename UCIt, typename UCSen, typename UFn, typename Size>(
330339
transform_iterator<UIt, UFn> iter,
331340
transform_sentinel<UIt, USen, UFn> sen,
332341
transform_iterator<UCIt, UFn> citer,
333342
transform_sentinel<UCIt, UCSen, UFn> csen,
334343
Size size,
335-
Fn_ new_fn)
344+
Fn new_fn)
336345
{
337-
return transform_borrow_impl(std::move(iter).base(),
338-
std::move(sen).base(),
339-
std::move(citer).base(),
340-
std::move(csen).base(),
341-
size,
342-
transform::nest_fn{std::move(iter).func(), std::move(new_fn)});
346+
return decltype(impl){}(std::move(iter).base(),
347+
std::move(sen).base(),
348+
std::move(citer).base(),
349+
std::move(csen).base(),
350+
size,
351+
transform::nest_fn{std::move(iter).func(), std::move(new_fn)});
343352
}}(radr::begin(urange),
344353
radr::end(urange),
345354
radr::cbegin(urange),
346355
radr::cend(urange),
347356
detail::size_or_not(urange),
348357
std::move(fn));
358+
// clang-format on
349359
};
350360

351-
inline constexpr auto transform_coro =
352-
[]<std::ranges::input_range URange, std::move_constructible Fn>(URange && urange, Fn fn)
353-
requires(std::invocable<Fn &, std::ranges::range_reference_t<URange>> &&
354-
can_reference<std::invoke_result_t<Fn &, std::ranges::range_reference_t<URange>>>)
361+
inline constexpr auto transform_coro = []<std::ranges::input_range URange, typename Fn>(URange && urange, Fn fn)
355362
{
363+
static_assert(std::move_constructible<Fn> && std::invocable<Fn &, std::ranges::range_reference_t<URange>>,
364+
"The constraints for radr::transform's functor are not met.");
365+
366+
using ref_t = std::invoke_result_t<Fn &, std::ranges::range_reference_t<URange>>;
367+
static_assert(can_reference<std::invoke_result_t<Fn &, std::ranges::range_reference_t<URange>>>,
368+
"The constraints for radr::transform's functor are not met.");
369+
356370
static_assert(!std::is_lvalue_reference_v<URange>, RADR_ASSERTSTRING_RVALUE);
357371
static_assert(std::movable<URange>, RADR_ASSERTSTRING_MOVABLE);
358372

359373
// we need to create inner functor so that it can take by value
360-
return
361-
[](auto urange_, Fn fn) -> radr::generator<std::invoke_result_t<Fn &, std::ranges::range_reference_t<URange>>>
374+
return [](auto urange_, Fn fn) -> radr::generator<ref_t, std::remove_cvref_t<ref_t>>
362375
{
363376
for (auto && elem : urange_)
364377
co_yield fn(std::forward<decltype(elem)>(elem));

tests/unit/rad/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ radr_unit_test(as_rvalue)
33
radr_unit_test(to_common)
44
radr_unit_test(drop)
55
radr_unit_test(drop_while)
6+
radr_unit_test(elements)
67
radr_unit_test(filter)
78
radr_unit_test(join)
89
radr_unit_test(to_single_pass)

0 commit comments

Comments
 (0)