98 lines
2.4 KiB
C++
98 lines
2.4 KiB
C++
|
#pragma once
|
||
|
|
||
|
#include "connection.hpp"
|
||
|
|
||
|
#include <coroutine>
|
||
|
#include <vector>
|
||
|
|
||
|
struct irc_promise;
|
||
|
|
||
|
struct irc_coroutine : std::coroutine_handle<irc_promise> {
|
||
|
using promise_type = irc_promise;
|
||
|
|
||
|
auto start(Connection &connection) -> void;
|
||
|
auto is_running() -> bool;
|
||
|
auto exception() -> std::exception_ptr;
|
||
|
};
|
||
|
|
||
|
struct irc_promise
|
||
|
{
|
||
|
// Pointer to the connection while running. Cleared on termination.
|
||
|
std::shared_ptr<Connection> connection_;
|
||
|
|
||
|
// Pointer to exception that terminated this coroutine if there is one.
|
||
|
std::exception_ptr exception_;
|
||
|
|
||
|
irc_coroutine get_return_object()
|
||
|
{
|
||
|
return {irc_coroutine::from_promise(*this)};
|
||
|
}
|
||
|
|
||
|
// Suspend waiting for start() to initialize connection_
|
||
|
std::suspend_always initial_suspend() noexcept { return {}; }
|
||
|
|
||
|
// Suspend so that is_running() and exception() work
|
||
|
std::suspend_always final_suspend() noexcept { return {}; }
|
||
|
|
||
|
// Normal termination
|
||
|
void return_void() {
|
||
|
connection_.reset();
|
||
|
}
|
||
|
|
||
|
// Abnormal termination - remember the exception
|
||
|
void unhandled_exception() {
|
||
|
connection_.reset();
|
||
|
exception_ = std::current_exception();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/*
|
||
|
struct wait_ircmsg {
|
||
|
using result_type = std::pair<IrcCommand, const IrcMsg &>
|
||
|
std::vector<IrcCommand> want_cmds_;
|
||
|
};
|
||
|
|
||
|
struct wait_timeout {
|
||
|
struct result_type {};
|
||
|
std::vector<IrcCommand> want_cmds_;
|
||
|
};
|
||
|
|
||
|
*/
|
||
|
|
||
|
class wait_command {
|
||
|
std::vector<IrcCommand> want_cmds_;
|
||
|
|
||
|
IrcCommand resultCmd;
|
||
|
const IrcMsg *resultMsg;
|
||
|
|
||
|
boost::signals2::scoped_connection ircmsg_connection_;
|
||
|
boost::signals2::scoped_connection disconnect_connection_;
|
||
|
|
||
|
void unsubscribe() {
|
||
|
ircmsg_connection_.disconnect();
|
||
|
disconnect_connection_.disconnect();
|
||
|
}
|
||
|
|
||
|
public:
|
||
|
wait_command(std::initializer_list<IrcCommand> want_cmds)
|
||
|
: want_cmds_(want_cmds)
|
||
|
, resultMsg{}
|
||
|
{}
|
||
|
|
||
|
/// The coroutine always needs to wait for a message. It will never
|
||
|
/// be ready immediately.
|
||
|
bool await_ready() noexcept { return false; }
|
||
|
|
||
|
/// Install event handles in the connection that will resume this coroutine.
|
||
|
void await_suspend(std::coroutine_handle<irc_promise> handle);
|
||
|
|
||
|
auto await_resume() -> std::pair<IrcCommand, const IrcMsg &> {
|
||
|
if (resultMsg) {
|
||
|
return std::make_pair(resultCmd, std::cref(*resultMsg));
|
||
|
} else {
|
||
|
throw std::runtime_error{"connection terminated"};
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|