-
Notifications
You must be signed in to change notification settings - Fork 0
/
tri_list.h
136 lines (111 loc) · 4.53 KB
/
tri_list.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#ifndef TRI_LIST_H
#define TRI_LIST_H
#include <numeric>
#include <variant>
#include <vector>
#include <functional>
#include "tri_list_concepts.h"
// ensure type T is exactly one of T1, T2, T3
template <typename T, typename T1, typename T2, typename T3>
concept OneOf =
(std::same_as<T, T1> && !std::same_as<T, T2> && !std::same_as<T, T3>)
|| (!std::same_as<T, T1> && std::same_as<T, T2> && !std::same_as<T, T3>)
|| (!std::same_as<T, T1> && !std::same_as<T, T2> && std::same_as<T, T3>);
// główna klasa zadania
template <typename T1, typename T2, typename T3>
class tri_list : public std::ranges::view_interface<tri_list<T1, T2, T3>> {
using elt_t = std::variant<T1, T2, T3>;
template <typename T>
using mod_t = std::function<T(const T&)>;
template <typename T>
using mods_t = std::vector<mod_t<T>>;
std::vector<elt_t> buffer;
// a) istotna decyzja projektowa: nie bedziemy uzywac tutaj funkcji compose
// i sprytnie komponowac kolejnych modyfikacji - to powoduje duzo alokacji
// zamiast tego bedziemy trzymac wektory tych modyfikacji
// (na ok0_example.cc daje to 31 alokacji, a to mniej niż 4 miliony)
// b) uzywamy tupli, zeby moc potem uzyc std::get<T>(tuple), co
// jest zdecydowanie najładniejszym sposobem na wyciągnięcie zmiennej po typie
std::tuple<mods_t<T1>, mods_t<T2>, mods_t<T3>> mods_containers;
// zwroc kontener trzymający kolejne modyfikacje dla typu T
template <OneOf<T1, T2, T3> T>
const std::vector<mod_t<T>>& get_modifiers() const {
return std::get<mods_t<T>>(mods_containers);
}
// niestety zrobienie tego overloadu "sprytnie", tj. jakimś const-castem
// skutkuje u mnie segfaultem - nie jestem pewien jak to zrobić bez kopiowana kodu
template <OneOf<T1, T2, T3> T>
std::vector<mod_t<T>>& get_modifiers() {
return std::get<mods_t<T>>(mods_containers);
}
template <OneOf<T1, T2, T3> T>
T apply_modifiers(T t) const {
const std::vector<mod_t<T>>& modifiers = get_modifiers<T>();
return std::accumulate(
modifiers.begin(),
modifiers.end(),
t,
[](T t, mod_t<T> f) { return std::invoke(f, t); }
);
}
// ta klase bym wolal zdefiniowac na zewnatrz, ale nie umiem
class iterator {
using base_iterator = typename decltype(buffer)::const_iterator;
const tri_list* tri_lst;
base_iterator base;
public:
using iterator_category = typename base_iterator::iterator_category;
using difference_type = typename base_iterator::difference_type;
using value_type = elt_t;
using pointer = elt_t*;
using reference = elt_t&;
iterator() = default;
explicit iterator(const tri_list* tri_lst, const base_iterator& other)
: tri_lst(tri_lst), base(other) {}
elt_t operator*() const {
return std::visit(
[this](auto x) -> elt_t { return tri_lst->apply_modifiers(x); },
*base
);
}
iterator& operator++() { base++; return *this; }
iterator& operator--() { base--; return *this; }
iterator operator++(int) { iterator result {*this}; ++this; return result; }
iterator operator--(int) { iterator result {*this}; --this; return result; }
friend bool operator==(const iterator& a, const iterator& b) { return a.base == b.base; }
};
public:
tri_list() = default;
tri_list(std::initializer_list<elt_t> elements) : buffer(elements) {};
template <OneOf<T1, T2, T3> T>
void push_back(const T& t) { buffer.push_back(t); }
template <OneOf<T1, T2, T3> T, modifier<T> F>
void modify_only(F m = F {}) {
get_modifiers<T>().push_back(m);
}
template <OneOf<T1, T2, T3> T>
void reset() {
get_modifiers<T>().clear();
}
template <OneOf<T1, T2, T3> T>
auto range_over() const {
return buffer
| std::ranges::views::filter(std::holds_alternative<T, T1, T2, T3>)
// ja bym wolal tak, ale to nie dziala
// | std::ranges::views::transform(std::get<T>)
| std::ranges::views::transform([](elt_t x){ return std::get<T>(x); })
| std::ranges::views::transform([&](T x){ return apply_modifiers<T>(x); });
}
iterator begin() const { return iterator(this, buffer.begin()); };
iterator begin() { return iterator(this, buffer.begin()); };
iterator end() const { return iterator(this, buffer.end()); }
iterator end() { return iterator(this, buffer.end()); }
};
template <typename T>
inline T identity(T x) { return x; }
static_assert(modifier<decltype(identity<int>), int>);
template <typename T, modifier<T> F, modifier<T> G>
inline auto compose(F f, G g) {
return [f, g](T t) mutable { return f(g(std::forward<T>(t))); };
}
#endif // TRI_LIST_H