std::coroutine_traits
| Definido en el archivo de encabezado <coroutine>
|
||
template< class R, class... Args > struct coroutine_traits; |
(desde C++20) | |
Determina el tipo de promesa a partir del tipo de retorno y los tipos de los parámetros de una corrutina. La biblioteca estándar proporciona un tipo miembro públicamente accesible promise_type igual que R::promise_type si el id-calificado es válido y denota un tipo; de lo contrario, no tiene tal miembro.
Las especializaciones definidas por el programa de coroutine_traits deberán definir un tipo miembro públicamente accesible promise_type; de lo contrario, el comportamiento no está definido.
Parámetros de plantilla
| R | - | Tipo de retorno de la corrutina. |
| Args | - | Los tipos de los parámetros de la corrutina, incluyendo el parámetro objeto implícito parámetro objeto implícito si la corrutina es una función miembro no estática. |
Tipos miembro
| Tipo | Definición |
promise_type
|
R::promise_type si es válido, o proporcionado por las especializaciones definidas por el programa.
|
Posible implementación
template<class, class...>
struct coroutine_traits {};
template<class R, class... Args>
requires requires { typename R::promise_type }
struct coroutine_traits<R, Args...> {
using promise_type = R::promise_type;
};
|
Notas
Si la corrutina es una función miembro no estática, entonces el primer tipo en Args... es el tipo del parámetro objeto implícito, y el resto son los tipos de los parámetros de la función (si los hay).
Si std::coroutine_traits<R, Args...>::promise_type no existe o no es un tipo clase, la definición correspondiente de la corrutina está mal formada.
Los usuarios pueden definir especializaciones parciales o explícitas (totales) de coroutine_traits dependientes de los tipos definidos por el programa para evitar la modificación a los tipos de retorno.
Ejemplo
#include <chrono>
#include <coroutine>
#include <exception>
#include <future>
#include <iostream>
#include <thread>
#include <type_traits>
// Habilitar el uso de std::future<T> como un tipo corrutina
// usando a std::promise<T> como el tipo promesa.
template <typename T, typename... Args>
requires(!std::is_void_v<T> && !std::is_reference_v<T>)
struct std::coroutine_traits<std::future<T>, 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());
}
};
};
// Lo mismo para std::future<void>.
template <typename... Args>
struct std::coroutine_traits<std::future<void>, 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());
}
};
};
// Permitir la espera con co_await para std::future<T> y std::future<void>
// ingenuamente iniciando un nuevo hilo para cada 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)};
}
// Utilizar la infraestructura que hemos establecido.
std::future<int> compute() {
int a = co_await std::async([] { return 6; });
int b = co_await std::async([] { return 7; });
co_return a * b;
}
std::future<void> fail() {
throw std::runtime_error("fuchi");
co_return;
}
int main() {
std::cout << compute().get() << '\n';
try {
fail().get();
} catch (const std::runtime_error &e) {
std::cout << "error: " << e.what() << '\n';
}
}
Salida:
42
error: fuchi