violet
Simple, cross-platform graphics API
Loading...
Searching...
No Matches
result.h
1#ifndef VIOLET_RESULT_H
2#define VIOLET_RESULT_H
3
4#include <assert.h>
5#include <fmt/core.h>
6
7#include <stdexcept>
8#include <type_traits>
9#include <variant>
10
11namespace violet {
12
13struct Unit {};
14
15template<typename T, typename E>
16class [[nodiscard]] Result {
17 public:
18 Result(const T& value) : Result(ok_tag {}, value) {}
19
20 Result(T&& value) : Result(ok_tag {}, std::move(value)) {}
21
22 template<typename U = E, typename = std::enable_if_t<!std::is_same_v<T, U>>>
23 Result(const E& error) : Result(err_tag {}, error) {}
24
25 template<typename U = E, typename = std::enable_if_t<!std::is_same_v<T, U>>>
26 Result(E&& error) : Result(err_tag {}, std::move(error)) {}
27
28 bool is_ok() const {
29 return std::holds_alternative<T>(m_data);
30 }
31
32 bool is_err() const {
33 return std::holds_alternative<E>(m_data);
34 }
35
36 const T& expect(const std::string& message) const& {
37 if (is_ok()) {
38 return unwrap();
39 }
40 fmt::print(stderr, "{}: {}\n", message, unwrap_err().message());
41 std::abort();
42 }
43
44 T&& expect(const std::string& message) && {
45 if (is_ok()) {
46 return std::move(*this).unwrap();
47 }
48 fmt::print(stderr, "{}: {}\n", message, unwrap_err().message());
49 std::abort();
50 }
51
52 const T& unwrap() const& {
53 assert(is_ok() && "Called unwrap on an Err value");
54 return std::get<T>(m_data);
55 }
56
57 T&& unwrap() && {
58 assert(is_ok() && "Called unwrap on an Err value");
59 return std::move(std::get<T>(m_data));
60 }
61
62 const T& unwrap_ok() const& {
63 return unwrap();
64 }
65
66 T&& unwrap_ok() && {
67 return std::move(*this).unwrap();
68 }
69
70 const E& unwrap_err() const& {
71 assert(is_err() && "Called unwrap_err on an Ok value");
72 return std::get<E>(m_data);
73 }
74
75 E&& unwrap_err() && {
76 assert(is_err() && "Called unwrap_err on an Ok value");
77 return std::move(std::get<E>(m_data));
78 }
79
80 template<typename F>
81 auto map(F&& f) const -> Result<decltype(f(std::declval<T>())), E> {
82 if (is_ok()) {
83 return Result<decltype(f(std::declval<T>())), E>(f(unwrap()));
84 }
85 return Result<decltype(f(std::declval<T>())), E>(unwrap_err());
86 }
87
88 template<typename F>
89 auto map_err(F&& f) const -> Result<T, decltype(f(std::declval<E>()))> {
90 if (is_err()) {
91 return Result<T, decltype(f(std::declval<E>()))>(f(unwrap_err()));
92 }
93 return Result<T, decltype(f(std::declval<E>()))>(unwrap());
94 }
95
96 template<typename F>
97 auto and_then(F&& f) const {
98 if (is_ok()) {
99 if constexpr (is_result<decltype(f(std::declval<T>()))>::value) {
100 return f(unwrap());
101 } else {
102 return Result<decltype(f(std::declval<T>())), E>(f(unwrap()));
103 }
104 }
105 return Result<decltype(f(std::declval<T>())), E>(unwrap_err());
106 }
107
108 template<typename U>
109 T unwrap_or(U&& default_value) const {
110 if (is_ok()) {
111 return unwrap();
112 }
113 return static_cast<T>(std::forward<U>(default_value));
114 }
115
116 template<typename F>
117 T unwrap_or_else(F&& f) const {
118 if (is_ok()) {
119 return unwrap();
120 }
121 return f(unwrap_err());
122 }
123
124 private:
125 std::variant<T, E> m_data;
126
127 struct ok_tag {};
128
129 struct err_tag {};
130
131 template<typename U>
132 struct is_result: std::false_type {};
133
134 template<typename U, typename V>
135 struct is_result<Result<U, V>>: std::true_type {};
136
137 Result(ok_tag, const T& value) : m_data(value) {}
138
139 Result(ok_tag, T&& value) : m_data(std::move(value)) {}
140
141 Result(err_tag, const E& error) : m_data(error) {}
142
143 Result(err_tag, E&& error) : m_data(std::move(error)) {}
144};
145
146template<typename T, typename E>
147Result<T, E> Ok(T&& value) {
148 return Result<T, E>(std::forward<T>(value));
149}
150
151template<typename T, typename E>
152Result<T, E> Err(E&& error) {
153 return Result<T, E>(std::forward<E>(error));
154}
155
156// MSVC compat :(
157#define TRY_ASSIGN(var, expr) \
158 auto var##_result = (expr); \
159 if (!var##_result.is_ok()) \
160 return std::move(var##_result).unwrap_err(); \
161 auto var = var##_result.unwrap();
162
163#define TRY_ASSIGN_TYPE(type, var, expr) \
164 auto&& _result_##var = (expr); \
165 if (!_result_##var.is_ok()) \
166 return std::move(_result_##var).unwrap_err(); \
167 type var = _result_##var.unwrap();
168
169#define TRY_ASSIGN_MOVE(var, expr) \
170 auto var##_result = (expr); \
171 if (!var##_result.is_ok()) \
172 return std::move(var##_result).unwrap_err(); \
173 auto var = std::move(var##_result).unwrap();
174
175#define TRY_ASSIGN_INTO(var, expr) \
176 auto var##_result = (expr); \
177 if (!var##_result.is_ok()) \
178 return std::move(var##_result).unwrap_err(); \
179 var = std::move(var##_result).unwrap();
180
181#if defined(_MSC_VER)
182 #define TRY(expr) \
183 do { \
184 auto&& _result = (expr); \
185 if (!_result.is_ok()) \
186 return std::move(_result).unwrap_err(); \
187 _result.unwrap(); \
188 } while (0)
189#else
190 // Unfortunately, this, which is much nicer
191 // doesn't play too well with MSVC
192 // auto lhs = TRY(expr);
193 #define TRY(expr) \
194 ({ \
195 auto _result = (expr); \
196 if (!_result.is_ok()) \
197 return _result.unwrap_err(); \
198 _result.unwrap(); \
199 })
200 #define TRY_MOVE(expr) \
201 ({ \
202 auto&& _result = (expr); \
203 if (_result.is_err()) \
204 return _result.unwrap_err(); \
205 std::move(_result).unwrap_ok(); \
206 })
207#endif
208
209} // namespace violet
210
211#endif
Definition result.h:16
Definition result.h:13