shortener-bot/DPP/include/dpp/coro/async.h

190 lines
5.9 KiB
C++

/************************************************************************************
*
* 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 <dpp/utility.h>
#include <dpp/coro/awaitable.h>
namespace dpp {
struct async_dummy : awaitable_dummy {
std::shared_ptr<int> dummy_shared_state = nullptr;
};
}
#ifdef DPP_CORO
#include "coro.h"
#include <utility>
#include <type_traits>
#include <functional>
#include <atomic>
#include <cstddef>
namespace dpp {
namespace detail {
namespace async {
/**
* @brief Shared state of the async and its callback, to be used across threads.
*/
template <typename R>
struct callback {
/**
* @brief Promise object to set the result into
*/
std::shared_ptr<basic_promise<R>> promise{nullptr};
/**
* @brief Call operator, sets the value in the promise and notifies any awaiter
*
* @param v Callback value
*/
template <typename U = R>
void operator()(const U& v) const requires (std::convertible_to<const U&, R>) {
promise->set_value(v);
}
/**
* @brief Call operator, sets the value in the promise and notifies any awaiter
*
* @param v Callback value
*/
template <typename U = R>
void operator()(U&& v) const requires (std::convertible_to<U&&, R>) {
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<R>)
{
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 <a href="https://github.com/brainboxdotcc/DPP/issues">GitHub issues</a> or to the <a href="https://discord.gg/dpp">D++ Discord server</a>.
* @tparam R The return type of the API call. Defaults to confirmation_callback_t
*/
template <typename R>
class async : public awaitable<R> {
/**
* @brief Callable object to pass to API calls
*/
detail::async::callback<R> api_callback{};
/**
* @brief Internal promise constructor, grabs a promise object for the callback to use
*/
explicit async(std::shared_ptr<basic_promise<R>> &&promise) : awaitable<R>{promise.get()}, api_callback{std::move(promise)} {}
public:
using awaitable<R>::awaitable; // use awaitable's constructors
using awaitable<R>::operator=; // use async_base's assignment operator
using awaitable<R>::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 <a href="https://en.cppreference.com/w/cpp/utility/functional/invoke">std::invoke</a> 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 <typename Obj, typename Fun, typename... Args>
#ifndef _DOXYGEN_
requires std::invocable<Fun, Obj, Args..., std::function<void(R)>>
#endif
explicit async(Obj &&obj, Fun &&fun, Args&&... args) : async{std::make_shared<basic_promise<R>>()} {
std::invoke(std::forward<Fun>(fun), std::forward<Obj>(obj), std::forward<Args>(args)..., api_callback);
}
/**
* @brief Construct an async object wrapping an invokeable object, the call is made immediately by forwarding to <a href="https://en.cppreference.com/w/cpp/utility/functional/invoke">std::invoke</a> and can be awaited later to retrieve the result.
*
* @param fun The object to call using <a href="https://en.cppreference.com/w/cpp/utility/functional/invoke">std::invoke</a>. 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 <typename Fun, typename... Args>
#ifndef _DOXYGEN_
requires std::invocable<Fun, Args..., std::function<void(R)>>
#endif
explicit async(Fun &&fun, Args&&... args) : async{std::make_shared<basic_promise<R>>()} {
std::invoke(std::forward<Fun>(fun), std::forward<Args>(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 */