result 0.0.1
A C++ result<T, E> type inspired by Rust
Loading...
Searching...
No Matches
result.h
Go to the documentation of this file.
1
13
14#ifndef RESULT_LIB
15#define RESULT_LIB
16
17#include <optional>
18#include <stdexcept>
19#include <utility>
20#include <variant>
21
22namespace res {
23
24template <typename T, typename E> class result;
25template <typename E> class result<void, E>;
26
30template <typename E> class err {
31 E error_;
32
33public:
34 err() = delete;
35 explicit err(E error) : error_(std::move(error)) {}
36
37 template <typename T> operator result<T, E>() const; // NOLINT(google-explicit-constructor)
38 operator result<void, E>() const; // NOLINT(google-explicit-constructor)
39};
40
41template <typename E> template <typename T> inline err<E>::operator result<T, E>() const {
43}
44
45template <typename E> inline err<E>::operator result<void, E>() const {
46 return result<void, E>(result<void, E>::Unsuccessful::UNSUCCESSFUL, error_);
47}
48
49} // namespace res
50
51#include <variant>
52
53namespace res {
54
55template <typename T, typename E> class result;
56template <typename E> class result<void, E>;
57
61template <typename T = std::monostate> class ok {
62 T value_;
63
64public:
65 ok() = default;
66 explicit ok(T value) : value_(std::move(value)) {}
67
68 template <typename E> operator result<T, E>() const; // NOLINT(google-explicit-constructor)
69 template <typename E> operator result<void, E>() const; // NOLINT(google-explicit-constructor)
70};
71
72template <typename T> template <typename E> inline ok<T>::operator result<T, E>() const {
74}
75
76template <typename T> template <typename E> inline ok<T>::operator result<void, E>() const {
77 return result<void, E>(result<void, E>::Successful::SUCCESSFUL);
78}
79
80} // namespace res
81
82namespace res {
83
90template <typename T, typename E> class result {
91 static_assert(!std::is_same_v<T, void>, "T (value type) must not be void");
92 static_assert(!std::is_same_v<E, void>, "E (error type) must not be void");
93
94 std::variant<T, E> content_;
95
96 // Simulate named constructors
97 enum Successful { SUCCESSFUL };
98 enum Unsuccessful { UNSUCCESSFUL };
99 result(Successful successful, T value) { content_.template emplace<0>(std::move(value)); }
100 result(Unsuccessful unsuccessful, E error) { content_.template emplace<1>(std::move(error)); }
101
102 template <typename U> friend class ok;
103 template <typename U> friend class err;
104
105public:
106 // Observers
107 [[nodiscard]] auto is_ok() const -> bool { return content_.index() == 0; }
108 explicit operator bool() const { return is_ok(); }
109 auto operator!() const -> bool { return !is_ok(); }
110
111 [[nodiscard]] auto value() const -> const T &;
112 [[nodiscard]] auto value_or(T &&default_value) const -> T;
113 [[nodiscard]] auto error() const -> const E &;
114
115 // Monadic operations
116 // map method gets functor F(T x) -> R as an argument and returns result of applying this functor to the value of the
117 // result object. If the result object is an error, the functor is not called and the error is propagated. But the
118 // result value type is changed to R.
119 template <typename F, typename R = std::invoke_result_t<F, T>> auto map(F &&functor) const -> result<R, E> {
120 if (is_ok()) { return ok<R>(std::forward<F>(functor)(value())); }
121 return err<E>(error());
122 }
123 // map_err method gets functor F(E x) -> U as an argument and returns result of applying this functor to the error of
124 // the result object. If the result object is a success, the functor is not called and the value is propagated. But
125 // the result error type is changed to U.
126 template <typename F, typename U = std::invoke_result_t<F, E>> auto map_err(F &&functor) const -> result<T, U> {
127 if (!is_ok()) { return err<U>(std::forward<F>(functor)(error())); }
128 return ok<T>(value());
129 }
130};
131
133template <typename E> class result<void, E> {
134 static_assert(!std::is_same_v<E, void>, "E (error type) must not be void");
135
136 std::optional<E> error_;
137
138 // Simulate named constructors
139 enum Successful { SUCCESSFUL };
140 enum Unsuccessful { UNSUCCESSFUL };
141 explicit result(Successful successful) {}
142 result(Unsuccessful unsuccessful, E error) : error_(std::move(error)) {}
143
144 template <typename U> friend class ok;
145 template <typename U> friend class err;
146
147public:
148 // Observers
149 [[nodiscard]] auto is_ok() const -> bool { return !error_.has_value(); }
150 explicit operator bool() const { return is_ok(); }
151 auto operator!() const -> bool { return !is_ok(); }
152
153 [[nodiscard]] auto error() const -> const E &;
154
155 // Monadic operations
156 // map method gets functor F() -> R as an argument and returns result of applying this functor to the value of the
157 // result object. If the result object is an error, the functor is not called and the error is propagated. But the
158 // result value type is changed to R.
159 template <typename F, typename R = std::invoke_result_t<F>> auto map(F &&functor) const -> result<R, E> {
160 if (is_ok()) { return ok<R>(std::forward<F>(functor)()); }
161 return err<E>(error_.value());
162 }
163 // map_err method for void value type is same as for non-void value type because it does not depend on the value type.
164 template <typename F, typename U = std::invoke_result_t<F, E>> auto map_err(F &&functor) const -> result<void, U> {
165 if (!is_ok()) { return err<U>(std::forward<F>(functor)(error_.value())); }
166 return ok();
167 }
168};
169
170template <typename T, typename E> inline auto result<T, E>::value() const -> const T & {
171 if (!is_ok()) { throw std::logic_error("value() called on result with error"); }
172 return std::get<0>(content_);
173}
174
175template <typename T, typename E> inline auto result<T, E>::value_or(T &&default_value) const -> T {
176 if (is_ok()) { return std::get<0>(content_); }
177 return std::move(default_value);
178}
179
180template <typename T, typename E> inline auto result<T, E>::error() const -> const E & {
181 if (is_ok()) { throw std::logic_error("error() called on result with value"); }
182 return std::get<1>(content_);
183}
184
185template <typename E> inline auto result<void, E>::error() const -> const E & {
186 if (is_ok()) { throw std::logic_error("error() called on result with value"); }
187 return error_.value();
188}
189
190} // namespace res
191
192#endif // RESULT_LIB
Err object represents an unsuccessful outcome and can be implicitly converted to a result.
Definition: result.h:30
Ok object represents a successful outcome and can be implicitly converted to a result.
Definition: result.h:61
result is a type that represents either success or failure.
Definition: result.h:90