-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmaybe.cpp
More file actions
143 lines (121 loc) · 3.08 KB
/
maybe.cpp
File metadata and controls
143 lines (121 loc) · 3.08 KB
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
/* This program was created in response to a question about whether there could be a better way to handle errors than
* the plain C way if you have no exceptions and no standard library. (The standard library includes here are for the
* final example, which relies on an RAII-ish type to demonstrate the necessity of ref-qualifiers to specify overloads
* for rvalues.
*
* Output:
* (&)
* m1 data: 42
* (&)
* (&)
* (&)
* m2 data data: 42
* m3 error: Nope.
* m4 error: Not here either.
* (&)
* m5 data: 69
* (&&)
* 666
* (&&)
* some string ineligible for short string optimization
*/
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <string>
#ifndef _NEW
inline void * operator new(size_t, void *p) noexcept {
return p;
}
#endif
/** Err is presumed to be contextually convertible to bool. */
template <typename T, typename Err = const char *>
class Maybe {
private:
// Nonsense to allow us to use non-default-constructible types for T.
struct alignas(T) Data {
char bytes[sizeof(T)];
};
Data data_;
Err error_{};
bool success_{};
Maybe() = default;
public:
Maybe(T data): success_(true) {
new (data_.bytes) T(static_cast<T &&>(data));
}
Maybe(Err err):
error_(static_cast<Err &&>(err)) {}
explicit inline operator bool() const {
return success_;
}
const Err & error() const {
return error_;
}
T & data() & {
printf("\x1b[2m(&)\x1b[22m\n");
if (!success_)
abort();
return *reinterpret_cast<T *>(data_.bytes);
}
T data() && {
printf("\x1b[2m(&&)\x1b[22m\n");
if (!success_)
abort();
return static_cast<T &&>(*reinterpret_cast<T *>(data_.bytes));
}
const T & data() const & {
if (!success_)
abort();
return *reinterpret_cast<const T *>(data_.bytes);
}
};
struct NoDefault {
int data;
NoDefault() = delete;
NoDefault(int data_):
data(data_) {}
};
int main() {
Maybe m1{42};
Maybe<Maybe<int>> m2{m1};
Maybe<float> m3{"Nope."};
Maybe<NoDefault> m4{"Not here either."};
Maybe<NoDefault> m5{69};
if (m1) {
printf("m1 data: %d\n", m1.data());
} else {
printf("m1 error: %s\n", m1.error());
}
if (auto error = m2.error()) {
printf("m2 error: %s\n", error);
} else if (m2.data()) {
printf("m2 data data: %d\n", m2.data().data());
} else {
printf("m2 data error: %s\n", m2.data().error());
}
if (auto error = m3.error()) {
printf("m3 error: %s\n", error);
} else {
printf("m3 data: %f\n", m3.data());
}
if (auto error = m4.error()) {
printf("m4 error: %s\n", error);
} else {
printf("m4 data: %d\n", m4.data().data);
}
if (auto error = m5.error()) {
printf("m5 error: %s\n", error);
} else {
printf("m5 data: %d\n", m5.data().data);
}
printf("%d\n", Maybe<int>(666).data());
std::cout << Maybe(std::string("some string ineligible for short string optimization")).data() << '\n';
// Ill-formed.
// Maybe<void> mv("Surely it's okay to use Maybe<void> if there's only an error!");
// if (auto error = mv.error()) {
// printf("mv error: %s\n", error);
// } else {
// printf("mv: no error.\n");
// }
}