8000 gh-2 core: Implement the `result` monad · AlexandruIca/monads-in-cpp@4ebe5da · GitHub
[go: up one dir, main page]

Skip to content

Commit 4ebe5da

Browse files
committed
gh-2 core: Implement the result monad
Also added a simple test in main
1 parent ccf9239 commit 4ebe5da

File tree

2 files changed

+192
-1
lines changed

2 files changed

+192
-1
lines changed

src/main.cpp

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,28 @@
11
#include <iostream>
2+
#include <string>
3+
4+
#include "result.hpp"
5+
6+
auto good_result() -> result<int, std::string>
7+
{
8+
return 5;
9+
}
10+
11+
auto bad_result() -> result<int, std::string>
12+
{
13+
using namespace std::string_literals;
14+
return "Error!"s;
15+
}
16+
17+
auto test_result() -> result<int, std::string>
18+
{
19+
int const a = co_await good_result();
20+
int const b = co_await bad_result();
21+
22+
co_return a + b;
23+
}
224

325
auto main() -> int
426
{
5-
std::cout << "Hello world!" << std::endl;
27+
std::cout << test_result() << std::endl;
628
}

src/result.hpp

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
#include <coroutine>
2+
#include <cstdlib>
3+
#include <iostream>
4+
#include <utility>
5+
#include <variant>
6+
7+
template<typename T, typename E>
8+
struct result
9+
{
10+
private:
11+
std::variant<std::monostate, T, E> m_data;
12+
13+
public:
14+
result() = default;
15+
result(result const&) = default;
16+
result(result&&) noexcept = default;
17+
~result() noexcept = default;
18+
19+
result(T const& value)
20+
: m_data{ value }
21+
{
22+
}
23+
result(E const& err)
24+
: m_data{ err }
25+
{
26+
}
27+
28+
auto operator=(result const&) -> result& = default;
29+
auto operator=(result&&) noexcept -> result& = default;
30+
31+
auto operator=(T const& value)
32+
{
33+
m_data = value;
34+
return *this;
35+
}
36+
37+
auto operator=(E const& err) -> result&
38+
{
39+
m_data = err;
40+
return *this;
41+
}
42+
43+
[[nodiscard]] auto value_or(T& fallback) -> T&
44+
{
45+
if(auto* p = std::get_if<T>(&m_data)) {
46+
return *p;
47+
}
48+
49+
return fallback;
50+
}
51+
52+
[[nodiscard]] auto value_or(T const& fallback) const -> T const&
53+
{
54+
if(auto* p = std::get_if<T>(&m_data)) {
55+
return *p;
56+
}
57+
58+
return fallback;
59+
}
60+
61+
[[nodiscard]] auto value() const -> T const&
62+
{
63+
if(auto* p = std::get_if<T>(&m_data)) {
64+
return *p;
65+
}
66+
67+
// Ugly solution, but we don't really care
68+
std::cout << "Tried to access either value even though it's in error state!" << std::endl;
69+
std::exit(EXIT_FAILURE);
70+
}
71+
72+
[[nodiscard]] auto get_underlying() noexcept -> std::variant<std::monostate, T, E>&
73+
{
74+
return m_data;
75+
}
76+
77+
[[nodiscard]] auto get_underlying() const noexcept -> std::variant<std::monostate, T, E>
78+
{
79+
return m_data;
80+
}
81+
82+
[[nodiscard]] inline auto is_error() const noexcept -> bool
83+
{
84+
return std::get_if<E>(&m_data);
85+
}
86+
87+
[[nodiscard]] auto get_error() noexcept -> E&
88+
{
89+
return *std::get_if<E>(&m_data);
90+
}
91+
92+
[[nodiscard]] auto get_error() const noexcept -> E const&
93+
{
94+
return *std::get_if<E>(&m_data);
95+
}
96+
97+
struct promise_type
98+
{
99+
result* res = nullptr;
100+
101+
auto get_return_object() -> result
102+
{
103+
return { *this };
104+
}
105+
106+
auto return_value(result val) -> void
107+
{
108+
*res = std::move(val);
109+
}
110+
111+
auto initial_suspend() const noexcept -> std::suspend_never
112+
{
113+
return {};
114+
}
115+
116+
auto final_suspend() const noexcept -> std::suspend_never
117+
{
118+
return {};
119+
}
120+
121+
auto unhandled_exception() noexcept -> void
122+
{
123+
}
124+
};
125+
126+
struct awaiter
127+
{
128+
result& res;
129+
130+
auto await_ready() -> bool
131+
{
132+
return !res.is_error();
133+
}
134+
135+
auto await_resume() -> T const
136+
{
137+
return res.value();
138+
}
139+
140+
auto await_suspend(std::coroutine_handle<promise_type> h) -> void
141+
{
142+
*(h.promise().res) = res;
143+
h.destroy();
144+
}
145+
};
146+
147+
auto operator co_await() -> awaiter
148+
{
149+
return awaiter{ *this };
150+
}
151+
152+
private:
153+
result(promise_type& promise)
154+
{
155+
promise.res = this;
156+
}
157+
};
158+
159+
template<typename T, typename E>
160+
auto operator<<(std::ostream& os, result<T, E> const& res) -> std::ostream&
161+
{
162+
if(res.is_error()) {
163+
os << res.get_error();
164+
}
165+
else {
166+
os << res.value();
167+
}
168+
return os;
169+
}

0 commit comments

Comments
 (0)
0