userver: userver/utils/enumerate.hpp Source File
Loading...
Searching...
No Matches
enumerate.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/utils/enumerate.hpp
4/// @brief @copybrief utils::enumerate
5/// @ingroup userver_universal
6
7#include <cstddef>
8#include <cstdint>
9#include <iterator>
10#include <type_traits>
11#include <utility>
12
13USERVER_NAMESPACE_BEGIN
14
15namespace utils::impl {
16
17template <typename Iter>
18auto DetectEnumerateValueType() -> std::pair<const std::size_t, decltype(*std::declval<Iter>())>;
19
20template <typename Iter, typename... Args>
21auto DetectEnumerateValueType(Args&&...) -> void;
22
23template <typename Iter>
24struct IteratorWrapper {
25 using difference_type = std::ptrdiff_t;
26 using value_type = decltype(DetectEnumerateValueType<Iter>());
27 using reference = value_type;
28 using iterator_category = std::input_iterator_tag;
29
30 Iter iterator;
31 std::size_t pos{0};
32
33 constexpr IteratorWrapper& operator++() {
34 ++pos;
35 ++iterator;
36 return *this;
37 }
38
39 constexpr value_type operator*() const { return {pos, *iterator}; }
40
41 template <typename OtherIter>
42 constexpr bool operator==(const IteratorWrapper<OtherIter>& other) const {
43 return iterator == other.iterator;
44 }
45
46 template <typename OtherIter>
47 constexpr bool operator!=(const IteratorWrapper<OtherIter>& other) const {
48 return !(iterator == other.iterator);
49 }
50};
51
52template <typename Range>
53using IteratorTypeOf = decltype(std::begin(std::declval<Range&>()));
54
55template <typename Range>
56using SentinelTypeOf = decltype(std::end(std::declval<Range&>()));
57
58template <typename Container>
59struct ContainerWrapper {
60 constexpr IteratorWrapper<IteratorTypeOf<Container>> begin() {
61 return {.iterator = std::begin(container), .pos = 0};
62 }
63
64 constexpr IteratorWrapper<SentinelTypeOf<Container>> end() { return {.iterator = std::end(container), .pos = 0}; }
65
66 constexpr IteratorWrapper<IteratorTypeOf<const Container>> begin() const {
67 return {.iterator = std::begin(container), .pos = 0};
68 }
69
70 constexpr IteratorWrapper<SentinelTypeOf<const Container>> end() const {
71 return {.iterator = std::end(container), .pos = 0};
72 }
73
74 Container container;
75};
76
77} // namespace utils::impl
78
79namespace utils {
80
81/// @brief Implementation of python-style enumerate function for range-for loops
82/// @param iterable: Container to iterate
83/// @returns ContainerWrapper, which iterator after dereference returns pair
84/// of index and (!!!)non-const reference to element(it seems impossible to make
85/// this reference const). It can be used in "range based for loop" with
86/// "structured binding" like this
87/// @code
88/// for (auto [pos, elem] : enumerate(someContainer)) {...}
89/// @endcode
90template <typename Container>
91constexpr auto enumerate(Container&& iterable) { // NOLINT(readability-identifier-naming)
92 return impl::ContainerWrapper<Container>{std::forward<Container>(iterable)};
93}
94
95} // namespace utils
96
97USERVER_NAMESPACE_END