// // Created by selim on 03.01.2022. // #ifndef AUTOCAT_GNOME_TASK_H #define AUTOCAT_GNOME_TASK_H #include #include #include #include #include #include #include #include class FutureException: public virtual std::exception, public virtual boost::exception { private: std::string _message; public: explicit FutureException(const char* msg): _message(msg) {} explicit FutureException(std::string msg): _message(std::move(msg)) {} const char* what() const noexcept { return _message.c_str(); } }; // Enable the use of std::future as a coroutine type // by using a std::promise as the promise type. template requires(!std::is_void_v && !std::is_reference_v) struct std::coroutine_traits, Args...> { struct promise_type : boost::promise { boost::future get_return_object() noexcept { return this->get_future(); } std::suspend_never initial_suspend() const noexcept { return {}; } std::suspend_never final_suspend() const noexcept { return {}; } void return_value(const T &value) noexcept(std::is_nothrow_copy_constructible_v) { this->set_value(value); } void return_value(T &&value) noexcept(std::is_nothrow_move_constructible_v) { this->set_value(std::move(value)); } void unhandled_exception() noexcept { this->set_exception(boost::current_exception()); } }; }; // Same for std::future. template struct std::coroutine_traits, Args...> { struct promise_type : boost::promise { boost::future get_return_object() noexcept { return this->get_future(); } std::suspend_never initial_suspend() const noexcept { return {}; } std::suspend_never final_suspend() const noexcept { return {}; } void return_void() noexcept { this->set_value(); } void unhandled_exception() noexcept { this->set_exception(boost::current_exception()); } }; }; template struct std::coroutine_traits { struct promise_type { void get_return_object() noexcept {} std::suspend_never initial_suspend() const noexcept { return {}; } std::suspend_never final_suspend() const noexcept { return {}; } void return_void() noexcept {} void unhandled_exception() noexcept { try { boost::rethrow_exception(boost::current_exception()); } catch (std::exception& ex) { std::cout << "Unhandled exception (in void coroutine) detected: " << ex.what() << std::endl; } } }; }; template auto operator co_await(boost::future future) noexcept requires(!std::is_reference_v) { struct awaiter : public boost::future { bool await_ready() const noexcept { return this->is_ready(); } void await_suspend(std::coroutine_handle<> cont) { this->then([this, cont](boost::future&& fut){ // future was moved out of awaiter to this lambda // so, we need to bring it back this->swap(fut); // Resume coroutine on the main thread (i.e. GMainContext) g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, awaiter::resumeContinuation, (gpointer)&cont, nullptr); }); } T await_resume() { return this->get(); } static int resumeContinuation(void* data) { auto handler = reinterpret_cast*>(data); if(*handler) { handler->resume(); } return G_SOURCE_REMOVE; } }; return awaiter{std::move(future)}; } #endif //AUTOCAT_GNOME_TASK_H