BRICKS
Small, useful blocks of code, to build bigger things.
Loading...
Searching...
No Matches
zip.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <cstddef>
4#include <iterator>
5#include <tuple>
6#include <type_traits>
7#include <vector>
8
9namespace bricks::detail {
10
11/* The index sequence is only used to deduce the Index sequence in the template
12 declaration. It uses a fold expression which is applied to the indexes,
13 using each expanded value to compare tuple value at that index. If any of
14 the tuple elements are equal, the function will return true. */
15template <typename... Args, std::size_t... Index>
16auto any_match_impl(std::tuple<Args...> const& lhs, std::tuple<Args...> const& rhs,
17 std::index_sequence<Index...> /* unused */) -> bool
18{
19 return (... || (std::get<Index>(lhs) == std::get<Index>(rhs)));
20}
21
25template <typename... Args>
26auto any_match(std::tuple<Args...> const& lhs, std::tuple<Args...> const& rhs) -> bool
27{
28 return any_match_impl(lhs, rhs, std::index_sequence_for<Args...>{});
29}
30
37template <typename Iter>
38using tuple_value_type =
39 std::conditional_t<std::is_same_v<Iter, std::vector<bool>::iterator> ||
40 std::is_same_v<Iter, std::vector<bool>::const_iterator>,
41 typename Iter::value_type, typename Iter::reference>;
42
43template <typename... Iters>
44class zip_iterator {
45 public:
46 using iterator_category = std::forward_iterator_tag;
47 using value_type = std::tuple<tuple_value_type<Iters>...>;
48 using difference_type = std::ptrdiff_t;
49 using pointer = value_type*;
50 using reference = value_type&;
51
52 zip_iterator() = delete;
53
54 explicit zip_iterator(Iters&&... iters) : ierators_{std::move(iters)...} {}
55
56 auto operator++() -> zip_iterator&
57 {
58 std::apply([](auto&&... args) { ((args++), ...); }, ierators_);
59 return *this;
60 }
61
62 auto operator++(int) -> zip_iterator
63 {
64 auto tmp = *this;
65 ++*this;
66 return tmp;
67 }
68
69 auto operator==(zip_iterator const& other) { return any_match(ierators_, other.ierators_); }
70 auto operator!=(zip_iterator const& other) { return !(*this == other); }
71
72 auto operator*() const -> value_type
73 {
74 return std::apply([](auto&&... args) { return value_type(*args...); }, ierators_);
75 }
76
77 private:
78 std::tuple<Iters...> ierators_;
79};
80
81/* std::decay needed because T is a reference, and is not a complete type */
82template <typename T>
83using select_iterator_for = std::conditional_t<std::is_const_v<std::remove_reference_t<T>>,
84 typename std::decay_t<T>::const_iterator,
85 typename std::decay_t<T>::iterator>;
86
87template <typename... T>
88class zipper {
89 public:
90 using iter_t = zip_iterator<select_iterator_for<T>...>;
91
92 explicit zipper(T&&... args) : args_{args...} {}
93
94 auto begin() const -> iter_t
95 {
96 return std::apply([](auto&&... args) { return iter_t(std::begin(args)...); }, args_);
97 }
98 auto end() const -> iter_t
99 {
100 return std::apply([](auto&&... args) { return iter_t(std::end(args)...); }, args_);
101 }
102
103 private:
104 std::tuple<T...> args_;
105};
106
107} // namespace bricks::detail