std:: coroutine_traits
|
Défini dans l'en-tête
<coroutine>
|
||
|
template
<
class
R,
class
...
Args
>
struct coroutine_traits ; |
(depuis C++20) | |
Détermine le type de promesse à partir du type de retour et des types de paramètres d'une coroutine. L'implémentation de la bibliothèque standard fournit un type membre accessible publiquement
promise_type
identique à
R::promise_type
si l'identifiant qualifié est valide et désigne un type. Sinon, il ne possède pas un tel membre.
Spécialisations définies par le programme
de
coroutine_traits
doivent définir un type imbriqué accessible publiquement
promise_type
, sinon le programme est mal formé.
Table des matières |
Paramètres du modèle
| R | - | type de retour de la coroutine |
| Args | - | types de paramètres de la coroutine, incluant le paramètre objet implicite si la coroutine est une fonction membre non statique |
Types imbriqués
| Nom | Définition |
promise_type
|
R::promise_type
s'il est valide, ou fourni par des spécialisations définies par le programme
|
Implémentation possible
namespace detail { template<class, class...> struct coroutine_traits_base {}; template<class R, class... Args> requires requires { typename R::promise_type; } struct coroutine_traits_base <R, Args...> { using promise_type = R::promise_type; }; } template<class R, class... Args> struct coroutine_traits : detail::coroutine_traits_base<R, Args...> {}; |
Notes
Si la coroutine est une fonction membre non statique, alors le premier type dans
Args...
est le type du paramètre objet implicite, et les autres sont les types de paramètres de la fonction (s'il y en a).
Si
std::coroutine_traits<R, Args...>::promise_type
n'existe pas ou n'est pas un type de classe, la définition de la coroutine correspondante est mal formée.
Les utilisateurs peuvent définir des spécialisations explicites ou partielles de
coroutine_traits
dépendant de types définis par le programme pour éviter la modification des types de retour.
Exemple
#include <chrono> #include <coroutine> #include <exception> #include <future> #include <iostream> #include <thread> #include <type_traits> // Un type défini par le programme sur lequel les spécialisations de coroutine_traits ci-dessous dépendent struct as_coroutine {}; // Activer l'utilisation de std::future<T> comme type de coroutine // en utilisant un std::promise<T> comme type de promesse. template<typename T, typename... Args> requires(!std::is_void_v<T> && !std::is_reference_v<T>) struct std::coroutine_traits<std::future<T>, as_coroutine, Args...> { struct promise_type : std::promise<T> { std::future<T> 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<T>) { this->set_value(value); } void return_value(T&& value) noexcept(std::is_nothrow_move_constructible_v<T>) { this->set_value(std::move(value)); } void unhandled_exception() noexcept { this->set_exception(std::current_exception()); } }; }; // Idem pour std::future<void>. template<typename... Args> struct std::coroutine_traits<std::future<void>, as_coroutine, Args...> { struct promise_type : std::promise<void> { std::future<void> 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(std::current_exception()); } }; }; // Permettre d'utiliser co_await sur std::future<T> et std::future<void> // en créant naïvement un nouveau thread pour chaque co_await. template<typename T> auto operator co_await(std::future<T> future) noexcept requires(!std::is_reference_v<T>) { struct awaiter : std::future<T> { bool await_ready() const noexcept { using namespace std::chrono_literals; return this->wait_for(0s) != std::future_status::timeout; } void await_suspend(std::coroutine_handle<> cont) const { std::thread([this, cont] { this->wait(); cont(); }).detach(); } T await_resume() { return this->get(); } }; return awaiter { std::move(future) }; } // Utiliser l'infrastructure que nous avons établie. std::future<int> compute(as_coroutine) { int a = co_await std::async([] { return 6; }); int b = co_await std::async([] { return 7; }); co_return a * b; } std::future<void> fail(as_coroutine) { throw std::runtime_error("bleah"); co_return; } int main() { std::cout << compute({}).get() << '\n'; try { fail({}).get(); } catch (const std::runtime_error& e) { std::cout << "erreur: " << e.what() << '\n'; } }
Sortie :
42 error: bleah