#pragma once #include "connection.hpp" #include #include struct irc_promise; struct irc_coroutine : std::coroutine_handle { 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_; // 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 std::vector want_cmds_; }; struct wait_timeout { struct result_type {}; std::vector want_cmds_; }; */ class wait_command { std::vector 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 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 handle); auto await_resume() -> std::pair { if (resultMsg) { return std::make_pair(resultCmd, std::cref(*resultMsg)); } else { throw std::runtime_error{"connection terminated"}; } } };