Войти
ПрограммированиеСтатьиОбщее

С++ корутины, начальный уровень (2 стр)

Автор:

Корутины в C++

Корутины были добавлены в стандарт C++20 и состоит из поддержки компилятором и минимальной реализацией в стандартной библиотеки.
В C++23 планируется добавить готовые корутины для работы с файлами, сетью и тд.
Для примера того что будет в C++23 можно посмотреть библиотеку cppcoro, разобраться в таком коде очень сложно, поэтому начинать надо с чего-то максимально простого.


Проверить поддержку корутин можно встроенными макросами:

#include <version>  // макросы определены в этом хэдере

#ifdef __cpp_impl_coroutine
    // поддерживается компилятором
#endif
#ifdef __cpp_lib_coroutine
    // есть в стандартной библиотеке
#   include <coroutine>
#endif

В cmake C++20 включается командой:

set_target_properties( "CoroutineSamples" PROPERTIES CXX_STANDARD 20 CXX_STANDARD_REQUIRED YES )
target_compile_features( "CoroutineSamples" PUBLIC cxx_std_20 )


Далее идет описание стандарта, оно не простое, поэтому лучше перейти к примерам, а к стандарту возвращаться по необходимости.


На данный момент в стандартной библиотеке поддерживается только минимальный набор, пригодный только для самостоятельного написания алгоритмов на корутинах.

Здесь используется понятие "концепт" - макет класса, где должны быть определенные методы и типы.


co_yield
Ключевое слово, используется внутри корутины для остановки выполнения корутины и передачи значения в Promise.
co_yield PromiseResult;


co_return
Ключевое слово, аналогично co_yield, передает значение и завершает выполнение корутины.
co_return; или co_return PromiseResult;


co_await
Унарный оператор, который позволяет остановить выполнение корутины. На вход принимает реализацию концепта Awaiter.
co_await Awaiter;


std::coroutine_handle<Promise>
Это шаблонный класс, который содержит адрес корутины и позволяет управлять ей.
Методы:

  • operator coroutine_handle<>() - конвертирует адрес корутины в общий тип.
  • operator bool() - проверяет содержит ли корутину или нулевой указатель, удаленная корутина вернет true, но упадет при использовании.
  • void operator(), void resume() - возобновляет выполнение корутины.
  • bool done() - возвращает true, если корутина завершена и остановлена, если корутина не была остановлена в конце, то она уже удалена и любой вызов приведет к падению.
  • Promise& promise() - возвращает вспомогательный контейнер для корутины, он нем далее.
  • void destroy() - удаляет корутину.
  • static coroutine_handle from_promise (Promise& p) - конвертирует Promise() в адрес корутины.
  • static coroutine_handle from_address (void *addr) - принимает nullptr или адрес корутины.


  • Концепт Promise
    Используется как параметр шаблона coroutine_handle<>, этот тип определяется пользователем.
    Память под него выделяется при создании корутины, получается что coroutine_handle<Promise> это указатель на Promise и память под локальные переменные корутины.
    Promise должен содержать методы, иначе будет ошибка компиляции:
  • Task get_return_object() - неявно вызывается для создания обертки над указателем на корутину.
  • Awaiter initial_suspend() - неявно вызывается перед выполнением корутины, аналогично co_await promise().initial_suspend().
  • void return_void() - неявно вызывается при использовании co_return.
  • void return_value(PromiseResult) - неявно вызывается при использовании co_return PromiseResult.
  • Awaiter yield_value(PromiseResult) - неявно вызывается при использовании co_yield PromiseResult.
  • Awaiter final_suspend() - неявно вызывается после выполнения корутины, аналогично co_await promise().final_suspend().
  • void unhandled_exception() - неявно вызывается при бросании исключения внутри корутины.
  • Awaiter await_transform(<expr>) - неявно вызывается при использовании co_await <expr>.
  • Как правильно писать реализацию Promise разбирается в примерах, это одна из больших и сложных частей корутин.


    Концепт Task
    Task это так корутина, которая возвращается функцией или лямбдой, он может не содержать ничего, а может хранить coroutine_handle<Promise> для ручного управления корутиной.
    Должен содержать тип promise_type с реализацией концепта Promise.


    Концепт Awaiter
    Класс задает логику остановки и возобновления корутины, второй по сложности тип в корутинах.
    Внутри корутины можно вызвать co_await <expr>, при этом компилятор ищет подходящую перегрузку:

  • Awaiter Task::operator co_await() - перегруженный оператор в Task, co_await Task разворачивается в Task.co_await().
  • Awaiter operator co_await (<expr>) - перегруженный глобальный оператор, co_await <expr> разворачивается в ::co_await(<expr>).
  • Awaiter Promise::await_transform (<expr>) - co_await <expr> разворачивается в promise().await_transform(<expr>).
  • std::suspend_never и std::suspend_always - реализация концепта Awaiter, они разбираются в примере 1.

    Страницы: 1 2 3 48 Следующая »

    #coroutine, #C++

    7 ноября 2022

    Комментарии [58]