Skip to content

Instantly share code, notes, and snippets.

Created July 8, 2017 10:22
Show Gist options
  • Save anonymous/a67ba4695c223a905ff108ed8b9a342f to your computer and use it in GitHub Desktop.
Save anonymous/a67ba4695c223a905ff108ed8b9a342f to your computer and use it in GitHub Desktop.
Abusing co_await for optionals in C++
// Changed awaiter to not leak memory on suspension thanks to /u/Enemii.
#include <experimental/coroutine>
#include <iostream>
#include <memory>
#include <optional>
#include <utility>
template<typename T>
class shared_optional {
std::shared_ptr<std::optional<T>> _opt;
public:
shared_optional() : _opt{std::make_shared<std::optional<T>>()} {}
auto& operator*() const { return **_opt; }
auto operator->() const { return &**_opt; }
explicit operator bool() const { return static_cast<bool>(*_opt); }
void reset() { _opt->reset(); }
template<typename U>
void emplace(U&& value) { _opt->emplace(std::forward<U>(value)); }
};
template<typename T, typename... Args>
struct std::experimental::coroutine_traits<shared_optional<T>, Args...> {
struct promise_type {
shared_optional<T> opt;
auto get_return_object() { return opt; }
auto initial_suspend() { return std::experimental::suspend_never{}; }
auto final_suspend() { return std::experimental::suspend_never{}; }
void set_exception(std::exception_ptr) { opt.reset(); }
void return_value(T value) { opt.emplace(std::move(value)); }
};
};
template<typename T>
auto operator co_await(const shared_optional<T>&& opt) {
struct awaiter {
const shared_optional<T>& input;
bool await_ready() { return static_cast<bool>(input); }
auto await_resume() { return *input; }
void await_suspend(std::experimental::coroutine_handle<> coro) { coro.destroy(); }
};
return awaiter{opt};
}
shared_optional<int> five() {
co_return 5;
}
shared_optional<int> six() {
co_return 6;
}
shared_optional<int> empty() {
return {};
}
shared_optional<int> sum() {
auto a = co_await five();
auto b = co_await six();
co_return a + b;
}
shared_optional<int> sum2() {
auto a = co_await five();
auto b = co_await empty();
co_return a + b;
}
// Output:
// Opt contains 11
// Opt2 is empty
int main() {
auto opt = sum();
if (opt) {
std::cout << "Opt contains " << *opt << "\n";
} else {
std::cout << "Opt is empty\n";
}
auto opt2 = sum2();
if (opt2) {
std::cout << "Opt2 contains " << *opt2 << "\n";
} else {
std::cout << "Opt2 is empty\n";
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment