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