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

Сопрограммы на С++, начальный уровень (6 стр)

Автор:

4. Как возвращать значение из корутины

+ код

Для возвращения значения из корутины используются ключевые слова co_yield и co_return.

co_return

struct promise_type {
    ResultType  _value;

    void  return_value (ResultType value) { _value = value; }
}
...
co_return -1;   // promise().return_value(-1)

  • Вызывается один раз при завершении выполнения корутины.
  • Внутри co_return <value>; вызывается return_value(), куда и передается значение.
  • co_yield

    struct promise_type {
        ResultType  _value;
    
        suspend_always  yield_value (ResultType value)  { _value = value;  return {}; }
    }
    ...
    co_yield 2;  // promise().yield_value(2)

  • Может вызываться множество раз внутри корутины.
  • Вызов co_yield может остановить выполнение корутины, это зависит от реализации.
  • Внутри co_yield <value>; вызывается yield_value(), куда и передается значение.
  • yield_value() может возвращать любую реализацию Awaiter, в данном примере возвращается suspend_always, чтобы остановить выполнение и прочитать значение снаружи.
  • Использование

    Для разнообразия корутина создается из лямбды.

    auto    create_promise = [] (int initial) -> Promise<int>
    {
        if ( initial == 0 )
            co_return -1;
    
        co_yield initial - 2;   // останавливает корутину
        co_yield initial - 1;   // останавливает корутину
        co_return initial;
    };
    
    auto    p0 = create_promise( 0 );
    auto    p1 = create_promise( 5 );
    
    int     r0_0 = p0();    // -1
    int     r0_1 = p0();    // -1
    int     r0_2 = p0();    // -1
    
    int     r1_0 = p1();    // 3
    int     r1_1 = p1();    // 4
    int     r1_2 = p1();    // 5


    Внутри оператора:

  • resume() возобновляет выполнение корутины.
  • После вызова co_yield или co_return в корутине выполнение оператора возобновляется.
  • Возвращается сохраненное значение _value.
  • ResultType  operator () ()
    {
        if ( not _handle.done() )
            _handle.resume();
    
        return _handle.promise()._value;
    }

    Исключения

    Если корутина кидает исключение, то оно ловится методом unhandled_exception() и сохраняется.

    struct promise_type {
        std::exception_ptr    _exception;
      
        void  unhandled_exception ()  { _exception = std::current_exception(); }
    }

    Сохраненное исключение проверяется вне корутины и снова бросается через rethrow_exception().

    ResultType  operator () ()
    {
        if ( not _handle.done() )
            _handle.resume();
    
        if ( _handle.promise()._exception )
            std::rethrow_exception( _handle.promise()._exception );
    
        return _handle.promise()._value;
    }

    Лямбда для создания корутины

    Разница между функциями-корутинами и лямбдами-корутинами в том, что для лямбды обязательно вызывать co_return в конце.

    Пример из cppreference показывает ошибку при доступе к удаленным данным в блоке захвата лямбды:

    void bad3()
    {
        coroutine h = [i = 0]() -> coroutine {
            std::cout << i;
            co_return;
        }();
        // лямбда удаляется
      
        h.resume(); // используется (anonymous lambda type)::i после удаления
    }

    Компилятор никак это не проверяет, а в данном примере падение может не произойти, так как стэк не затирается, а тип i - тривиальный. Использование сложных типов, например string, точно приведет к падению.

    Страницы: 13 4 5 6 7 8 Следующая »

    #C++

    7 ноября 2022 (Обновление: 11 мар 2024)

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