/************************************************************************************ * * D++, A Lightweight C++ library for Discord * * Copyright 2022 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ************************************************************************************/ #pragma once #include #include namespace dpp { struct async_dummy : awaitable_dummy { std::shared_ptr dummy_shared_state = nullptr; }; } #ifdef DPP_CORO #include "coro.h" #include #include #include #include #include namespace dpp { namespace detail { namespace async { /** * @brief Shared state of the async and its callback, to be used across threads. */ template struct callback { /** * @brief Promise object to set the result into */ std::shared_ptr> promise{nullptr}; /** * @brief Call operator, sets the value in the promise and notifies any awaiter * * @param v Callback value */ template void operator()(const U& v) const requires (std::convertible_to) { promise->set_value(v); } /** * @brief Call operator, sets the value in the promise and notifies any awaiter * * @param v Callback value */ template void operator()(U&& v) const requires (std::convertible_to) { promise->set_value(std::move(v)); } /** * @brief Call operator, sets the value in the promise and notifies any awaiter */ void operator()() const requires (std::is_void_v) { promise->set_value(); } }; } // namespace async } // namespace detail struct confirmation_callback_t; /** * @class async async.h coro/async.h * @brief A co_await-able object handling an API call in parallel with the caller. * * This class is the return type of the dpp::cluster::co_* methods, but it can also be created manually to wrap any async call. * * @remark - The coroutine may be resumed in another thread, do not rely on thread_local variables. * @warning - This feature is EXPERIMENTAL. The API may change at any time and there may be bugs. Please report any to GitHub issues or to the D++ Discord server. * @tparam R The return type of the API call. Defaults to confirmation_callback_t */ template class async : public awaitable { /** * @brief Callable object to pass to API calls */ detail::async::callback api_callback{}; /** * @brief Internal promise constructor, grabs a promise object for the callback to use */ explicit async(std::shared_ptr> &&promise) : awaitable{promise.get()}, api_callback{std::move(promise)} {} public: using awaitable::awaitable; // use awaitable's constructors using awaitable::operator=; // use async_base's assignment operator using awaitable::await_ready; // expose await_ready as public /** * @brief The return type of the API call. Defaults to confirmation_callback_t */ using result_type = R; /** * @brief Construct an async object wrapping an object method, the call is made immediately by forwarding to std::invoke and can be awaited later to retrieve the result. * * @param obj The object to call the method on * @param fun The method of the object to call. Its last parameter must be a callback taking a parameter of type R * @param args Parameters to pass to the method, excluding the callback */ template #ifndef _DOXYGEN_ requires std::invocable> #endif explicit async(Obj &&obj, Fun &&fun, Args&&... args) : async{std::make_shared>()} { std::invoke(std::forward(fun), std::forward(obj), std::forward(args)..., api_callback); } /** * @brief Construct an async object wrapping an invokeable object, the call is made immediately by forwarding to std::invoke and can be awaited later to retrieve the result. * * @param fun The object to call using std::invoke. Its last parameter must be a callable taking a parameter of type R * @param args Parameters to pass to the object, excluding the callback */ template #ifndef _DOXYGEN_ requires std::invocable> #endif explicit async(Fun &&fun, Args&&... args) : async{std::make_shared>()} { std::invoke(std::forward(fun), std::forward(args)..., api_callback); } /** * @brief Copy constructor is disabled. */ async(const async&) = delete; /** * @brief Move constructor, moves the awaitable async object */ async(async&&) = default; /** * @brief Copy assignment operator is disabled. */ async& operator=(const async&) = delete; /** * @brief Move assignment operator, moves the awaitable async object */ async& operator=(async&&) = default; /** * @brief Destructor, signals to the callback that the async object is gone and shouldn't be notified of the result */ ~async() { this->abandon(); } }; DPP_CHECK_ABI_COMPAT(async<>, async_dummy); } #endif /* DPP_CORO */