-
Notifications
You must be signed in to change notification settings - Fork 84
/
Copy pathvariadic.cpp
144 lines (119 loc) · 4.94 KB
/
variadic.cpp
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
137
138
139
140
141
142
143
144
#include <cassert>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <tuple>
// we start with the tuple from the slides on variadic templates
template <typename... Ts>
struct tuple {};
template <typename T, typename... Ts>
struct tuple<T, Ts...> : tuple<Ts...> {
// exercise 1: TODO
// exercise 2: TODO
tuple(T head, Ts... tail) : tuple<Ts...>(tail...), m_head(head) {}
T m_head;
};
// we need a deduction guide, because the compiler only searches the primary template for constructors
template <typename... Ts>
tuple(Ts...) -> tuple<Ts...>;
// here is a helper to get the tail of a tuple
template <typename Head, typename... Tail>
auto& tail(tuple<Head, Tail...>& t) {
// we just cast the tuple to its base class, which holds 1 element less
return static_cast<tuple<Tail...>&>(t);
}
// exercise 3: implement head(tuple) here
// exercise 4: implement get<I> here
// exercise 5: specialize std::tuple_size
// exercise 6: specialize std::tuple_element
// exercise 7: implement tuple_cat(tuple, tuple)
template <typename... Ts1, typename... Ts2>
auto tupleCat(tuple<Ts1...> t1, tuple<Ts2...> t2) {
// TODO
}
// exercise 8: implement sum(tuple)
template <typename... Ts>
auto sum(tuple<Ts...>& t) {
// TODO
}
// exercise 9: implement operator<< for tuple producing comma separated output
template <typename... Ts>
std::ostream& operator<<(std::ostream& os, tuple<Ts...>& t) {
// TODO
return os;
}
template <typename... Ts>
std::string toString(tuple<Ts...>& t) {
std::stringstream ss;
ss << std::boolalpha << t;
return ss.str();
}
int main() {
// tuple construction
tuple t1{};
tuple t2{42, 3.14f, false};
// exercise 1: make tuple default-constructible, so the following code compiles:
tuple<int, float> t3{};
// exercise 2: avoid implicit construction from a single value. make the following line not compile:
tuple t4 = 42;
// exercise 3: write head(tuple), which returns the first element of a tuple
static_assert(std::is_same_v<decltype(head(t2)), int&>);
assert(head(t2) == 42);
head(t2) = 43;
assert(head(t2) == 43);
// exercise 4: implement get<I>(tuple), use head() and tail() to help you
// 'if constexpr' may help you there
static_assert(std::is_same_v<decltype(get<0>(t2)), int&>);
static_assert(std::is_same_v<decltype(get<1>(t2)), float&>);
static_assert(std::is_same_v<decltype(get<2>(t2)), bool&>);
assert(get<0>(t2) == 43);
assert(get<1>(t2) == 3.14f);
assert(get<2>(t2) == false);
get<0>(t2) = 42;
assert(get<0>(t2) == 42);
// exercise 5: specialize std::tuple_size (used by std::tuple_size_v) for tuple
static_assert(std::tuple_size_v<decltype(t1)> == 0);
static_assert(std::tuple_size_v<decltype(t2)> == 3);
static_assert(std::tuple_size_v<decltype(t3)> == 2);
// exercise 6: specialize std::tuple_element (used by std::tuple_element_t),
// you can reuse your get<I>(tuple) to determine the I-th type
// and std::remove_reference_t to strip the reference
static_assert(std::is_same_v<std::tuple_element_t<0, decltype(t2)>, int>);
static_assert(std::is_same_v<std::tuple_element_t<1, decltype(t2)>, float>);
static_assert(std::is_same_v<std::tuple_element_t<2, decltype(t2)>, bool>);
// congratulations! structured bindings should now work for your tuple!
auto& [i, f, b] = t2;
assert(get<0>(t2) == 42);
assert(get<1>(t2) == 3.14f);
assert(get<2>(t2) == false);
i = 1337;
assert(get<0>(t2) == 1337);
// exercise 7: implement a binary tuple_cat(tuple, tuple).
// you will likely need std::index_sequence again, but now one for each tuple.
auto t4 = tupleCat(t2, t3);
static_assert(std::is_same_v<decltype(t4), tuple<int, float, bool, int, float>>);
assert(get<0>(t4) == 1337);
assert(get<1>(t4) == 3.14f);
assert(get<2>(t4) == false);
assert(get<3>(t4) == 0);
assert(get<4>(t4) == 0.0f);
// exercise 8: implement a function sum(tuple) that will add up all elements of the tuple
// use a fold expression and the lambda/index_sequence pattern
std::cout << "sum t1: " << sum(t1) << '\n';
std::cout << "sum t2: " << sum(t2) << '\n';
std::cout << "sum t4: " << sum(t4) << '\n';
assert(sum(t1) == 0);
assert(sum(t2) == 1340.14f); // false == 0
assert(sum(t4) == 1340.14f);
// exercise 9: make your operator<< produce exactly the following output.
// you may need a fold expression over the comma operator, since you need to output the ", " as well
// for each tuple element, except the last.
// use a helper function with index_sequence.
std::cout << std::boolalpha;
std::cout << t1 << '\n';
std::cout << t2 << '\n';
std::cout << t4 << '\n';
assert(toString(t1) == "tuple{}");
assert(toString(t2) == "tuple{1337, 3.14, false}");
assert(toString(t4) == "tuple{1337, 3.14, false, 0, 0}");
}