libQuotient
A Qt library for building matrix clients
Loading...
Searching...
No Matches
ranges_extras.h
Go to the documentation of this file.
1// SPDX-FileCopyrightText: The Quotient Project Contributors
2// SPDX-License-Identifier: LGPL-3.0-or-later
3
4#pragma once
5
6#include <ranges>
7
8namespace Quotient {
9
10//! \brief An indexOf() alternative for any range
11//!
12//! Unlike QList::indexOf(), returns `range.size()` if \p value is not found
13template <typename RangeT, typename ValT, typename ProjT = std::identity>
16[[nodiscard]] constexpr inline auto findIndex(const RangeT& range, const ValT& value,
17 ProjT proj = {})
18{
19 using namespace std::ranges;
21}
22
23//! \brief A replacement of std::ranges::to() while toolchains catch up
24//!
25//! Returns a container of type \p TargetT created from \p sourceRange. Unlike std::ranges::to(),
26//! you have to pass the range to it (e.g. `rangeTo<TargetT>(someRange)`); using it in a pipeline
27//! (`someRange | rangeTo<TargetT>()`) won't compile. Internally calls std::ranges::to() if it's
28//! available; otherwise, returns the result of calling
29//! `TargetT(ranges::begin(sourceRange), ranges::end(sourceRange))`.
30template <class TargetT, typename SourceT>
31[[nodiscard]] constexpr inline auto rangeTo(SourceT&& sourceRange)
32{
33#if defined(__cpp_lib_ranges_to_container)
34 return std::ranges::to<TargetT>(std::forward<SourceT>(sourceRange));
35#else
36 // Provide the minimal necessary subset of what std::ranges::to() can do
37 using namespace std::ranges;
38 if constexpr (std::constructible_from<TargetT, SourceT>)
39 return TargetT(std::forward<SourceT>(sourceRange));
40 else {
41 using iter_t = iterator_t<SourceT>;
42 using iter_category_t = typename std::iterator_traits<iter_t>::iterator_category;
43 if constexpr (requires {
44 requires common_range<SourceT>;
45 typename iter_category_t;
46 requires std::derived_from<iter_category_t, std::input_iterator_tag>;
47 requires std::constructible_from<TargetT, iter_t, sentinel_t<SourceT>>;
48 })
49 return TargetT(begin(sourceRange), end(sourceRange));
50 else {
51 TargetT c{};
52 if constexpr (sized_range<SourceT>
53 && requires(range_size_t<TargetT> n) { c.reserve(n); })
54 c.reserve(static_cast<range_size_t<TargetT>>(size(sourceRange)));
55 using ValT = std::iter_value_t<iter_t>;
56 for (auto&& e : sourceRange) {
57 if constexpr (requires { c.emplace_back(std::forward<ValT>(e)); })
58 c.emplace_back(std::forward<ValT>(e));
59 else if constexpr (requires { c.push_back(std::forward<ValT>(e)); })
60 c.push_back(std::forward<ValT>(e));
61 else if constexpr (requires { c.emplace(c.end(), std::forward<ValT>(e)); })
62 c.emplace(c.end(), std::forward<ValT>(e));
63 else
64 c.insert(c.end(), std::forward<ValT>(e));
65 }
66 return c;
67 }
68 }
69#endif
70}
71
72//! An overload that accepts unspecialised container template
73template <template <typename> class TargetT, typename SourceT>
74[[nodiscard]] constexpr inline auto rangeTo(SourceT&& sourceRange)
75{
76 // Avoid template argument deduction because Xcode still can't do it when TargetT is an alias
77#if defined(__cpp_lib_ranges_to_container)
78 return std::ranges::to<TargetT<std::ranges::range_value_t<SourceT>>>(
79 std::forward<SourceT>(sourceRange));
80#else
81 return rangeTo<TargetT<std::ranges::range_value_t<SourceT>>>(std::forward<SourceT>(sourceRange));
82#endif
83}
84
85#ifdef __cpp_lib_ranges_contains
86constexpr inline auto rangeContains = std::ranges::contains;
87#else
88[[nodiscard]] constexpr inline auto rangeContains(const auto& c, const auto& v, auto proj)
89{
90 return std::ranges::find(c, v, std::move(proj)) != std::ranges::end(c);
91}
92#endif
93
94} // namespace Quotient
constexpr auto rangeContains(const auto &c, const auto &v, auto proj)
constexpr auto rangeTo(SourceT &&sourceRange)
A replacement of std::ranges::to() while toolchains catch up.