[ Web Proxy ]
URL:
Viewing: https://ru.cppreference.com/cpp/language/coroutines [Back]  [Original]

(C++20) cppreference.com
cppreference.com

(C++20)

cppreference.com
 
C++
(C++20)
(C++20)
(C++11)
(C++20)
/
(C++11)
(C++11)
(C++11)
(C++17)
 
 
(lvalue, rvalue, xvalue)
(sequence points)
-(C++11)
(C++11)
(C++11)
:
:  
:
:
:
:
:
a=b, a+=b, a-=b, a*=b, a/=b, a%=b, a&=b, a|=b, a^=b, a<<=b, a>>=b
++a, --a, a++, a--
+a, -a, a+b, a-b, a*b, a/b, a%b, ~a, a&b, a|b, a^b, a<<b, a>>b
a||b, a&&b, !a
a==b, a!=b, a<b, a>b, a<=b, a>=b, a<=>b(C++20)
a[b], *a, &a, a->b, a.b, a->*b, a.*b
a(...), a,b, a?b:c
new
delete
throw
alignof
sizeof
sizeof...(C++11)
typeid
noexcept(C++11)
(C++17)
(C++20)
const_cast
static_cast
reinterpret_cast
dynamic_cast
: (T)a, T(a)
 

, , . : , , , , . , (, / ), .

, :

  • co_await
task<> tcp_echo_server() {
  char data[1024];
  while (true)
  {
    size_t n = co_await socket.async_read_some(buffer(data));
    co_await async_write(socket, buffer(data, n));
  }
}
  • co_yield, ,
generator<unsigned int> iota(unsigned int n = 0)
  while(true)
    co_yield n++;
}
  • co_return ,
lazy<int> f()
{
  co_return 7;
}

, , .

, return (auto ).

constexpr, , main .

  • , . .
  • , - . , , .
  • , , ( ), ,
  • ( )
  • , , , ,
  • ,

, :

  • , operator new ( )
  • : , ( , )
  • . , , . .
  • promise.get_return_object() . . , , , .
  • promise.initial_suspend() co_await- . Promise std::suspend_always, , std::suspend_never, .
  • co_await promise.initial_suspend() , .

, :

#include <coroutine>
#include <iostream>

struct promise;
struct coroutine : std::coroutine_handle<promise>
{
  using promise_type = ::promise;
};

struct promise
{
  coroutine get_return_object()
  { return {coroutine::from_promise(*this)}; }
  std::suspend_always initial_suspend() noexcept { return {}; }
  std::suspend_always final_suspend() noexcept { return {}; }
  void return_void() {}
  void unhandled_exception() {}
};

struct S
{
  int i;
  coroutine f()
  {
    std::cout << i;
    co_return;
  }
};

void bad1()
{
  coroutine h = S{0}.f();
  // S{0} 
  h.resume(); //    std::cout << i,  S::i
              //  
  h.destroy();
}

coroutine bad2()
{
  S s{0};
  return s.f(); //        
                //   
}

void bad3()
{
  coroutine h = [i = 0]() -> coroutine // ,    
  {
    std::cout << i;
    co_return;
  }(); //  
  //  
  h.resume(); //  (  )::i  
  h.destroy();
}

void good()
{
  coroutine h = [](int i) -> coroutine //  i  
  {
    std::cout << i;
    co_return;
  }(0);
  //  
  h.resume(); //  , i        
  h.destroy();
}

  • , , /, , .

co_return, :

  • promise.return_void()
  • co_return;
  • co_return ;, void
  • promise.return_value() co_return , void
  • , .
  • promise.final_suspend() co_await .

co_return;, , , Promise Promise::return_void(). , , , , , cv void.

// ,         
task<void> f() {
    //  ,  
}

task<void> g() {
    co_return;  // OK
}

task<void> h() {
    co_await g();
    // OK,  co_return;
}

, :

  • promise.unhandled_exception() catch
  • promise.final_suspend() co_await (, ). .

- , {{c/core|co_return} , - , , :

operator new .

Promise , , operator new.

Promise operator new, , , ( std ::size_t), , operator new ( )

operator new ( ),

  • ,

( ) ( )

, std::bad_alloc, Promise - Promise::get_return_object_on_allocation_failure(). - , operator new , , Promise::get_return_object_on_allocation_failure() , :

struct Coroutine::promise_type
{
    /* ... */


    //    new
    //   
    static Coroutine get_return_object_on_allocation_failure()
    {
        std::cerr << __func__ << '\n';
        throw std::bad_alloc(); // ,  Coroutine(nullptr);
    }


    //   new
    //   
    void* operator new(std::size_t n) noexcept
    {
        if (void* mem = std::malloc(n))
            return mem;
        return nullptr; //   
    }
};

Promise

Promise std::coroutine_traits.

R Args... , ClassT /*cv-*/ ( ) , , cv- , -, Promise :

  • std::coroutine_traits<R, Args...>::promise_type, -,
  • std::coroutine_traits<R, ClassT /*cv-*/&, Args...>::promise_type, -, rvalue,
  • std::coroutine_traits<R, ClassT /*cv-*/&&, Args...>::promise_type, -, rvalue.

:

... Promise ...
task<void> foo(int x); std::coroutine_traits<task<void>, int>::promise_type
task<void> Bar::foo(int x) const; std::coroutine_traits<task<void>, const Bar&, int>::promise_type
task<void> Bar::foo(int x) &&; std::coroutine_traits<task<void>, Bar&&, int>::promise_type

co_await

co_await . , (1) , operator co_await, operator co_await, (2) Promise::await_transform .

co_await

co_await

-, :

  • , yield, , .
  • , Promise - await_transform, promise.await_transform()
  • , .

:

  • operator co_await , :
  • awaitable.operator co_await() ,
  • operator co_await(static_cast<Awaitable&&>(awaitable)) , .
  • , co_await, , .
  • , ,

prvalue, . , glvalue, , .

awaiter.await_ready() ( , , , ). , bool, false,

( ).
awaiter.await_suspend(handle), handle , . , - ( )
  • await_suspend void, / ( ),
  • await_suspend ,
  • true /
  • false .
  • await_suspend - , ( handle.resume()) ( , , )
  • await_suspend , , ,

, awaiter.await_resume() ( , ), co_await .

co_await , awaiter.await_resume().

, awaiter.await_suspend(), . , , /. , , , , , await_suspend() , await_suspend() *this , .

#include <coroutine>
#include <iostream>
#include <stdexcept>
#include <thread>

auto switch_to_new_thread(std::jthread& out)
{
  struct awaitable
  {
    std::jthread* p_out;
    bool await_ready() { return false; }
    void await_suspend(std::coroutine_handle<> h)
    {
      std::jthread& out = *p_out;
      if (out.joinable())
        throw std::runtime_error("  jthread  ");
      out = std::jthread([h] { h.resume(); });
      //   :     *this
      // std::cout << "  : " << p_out->get_id() << '\n';
      std::cout << "  : " << out.get_id() << '\n'; //  OK
    }
    void await_resume() {}
  };
  return awaitable{&out};
}

struct task
{
  struct promise_type
  {
    task get_return_object() { return {}; }
    std::suspend_never initial_suspend() { return {}; }
    std::suspend_never final_suspend() noexcept { return {}; }
    void return_void() {}
    void unhandled_exception() {}
  };
};

task resuming_on_new_thread(std::jthread& out)
{
  std::cout << "   : " << std::this_thread::get_id() << '\n';
  co_await switch_to_new_thread(out);
  //   
  std::cout << "   : " << std::this_thread::get_id() << '\n';
}

int main()
{
  std::jthread out;
  resuming_on_new_thread(out);
}

:

   : 139972277602112
  : 139972267284224
   : 139972267284224

: ( , ) co_await. API- /, .

: std::suspend_always std::suspend_never.

co_yield

co_yield :

co_yield
co_yield ----

co_await promise.yield_value()

yield_value (/ , co_await) std::suspend_always, /.

#include <coroutine>
#include <exception>
#include <iostream>

template<typename T>
struct Generator
{
   //     'Generator',       .
   //        'co_yield'.
   //       'MyGenerator' (  ),
   //      promise_type  
   // 'MyGenerator get_return_object()'.
   // .      /
   //    .

  struct promise_type;
  using handle_type = std::coroutine_handle<promise_type>;

  struct promise_type // 
  {
    T value_;
    std::exception_ptr exception_;

    Generator get_return_object() {
      return Generator(handle_type::from_promise(*this));
    }
    std::suspend_always initial_suspend() { return {}; }
    std::suspend_always final_suspend() noexcept { return {}; }
    void unhandled_exception() { exception_ = std::current_exception(); } // 
                                                                          // 

    template<std::convertible_to<T> From> //  C++20
    std::suspend_always yield_value(From &&from)
    {
      value_ = std::forward<From>(from); //    
      return {};
    }
    void return_void() {}
  };

  handle_type h_;

  Generator(handle_type h) : h_(h) {}
  ~Generator() { h_.destroy(); }
  explicit operator bool()
  {
    fill(); //    ,      ,
            //      (co_yield)   
            //   C++ (operator () )  / 
            //   co_yield  (     ).
            //   /   ,   
            // (operator()      ).
    return !h_.done();
  }
  T operator()()
  {
    fill();
    full_ = false; //      ,
                   //     
    return std::move(h_.promise().value_);
  }

private:
  bool full_ = false;

  void fill()
  {
    if (!full_)
    {
      h_();
      if (h_.promise().exception_)
        std::rethrow_exception(h_.promise().exception_);
        //      

      full_ = true;
    }
  }
};

Generator<uint64_t>
fibonacci_sequence(unsigned n)
{

  if (n==0)
    co_return;

  if (n>94)
    throw std::runtime_error("   . "
                             "  .");

  co_yield 0;

  if (n==1)
    co_return;

  co_yield 1;

  if (n==2)
    co_return;

  uint64_t a=0;
  uint64_t b=1;

  for (unsigned i = 2; i < n; i++)
  {
    uint64_t s=a+b;
    co_yield s;
    a=b;
    b=s;
  }
}

int main()
{
  try {

    auto gen = fibonacci_sequence(10); //  94   uint64_t

    for (int j=0; gen; j++)
      std::cout << "fib("<<j <<")=" << gen() << '\n';

  }
  catch (const std::exception& ex)
  {
    std::cerr << ": " << ex.what() << '\n';
  }
  catch (...)
  {
    std::cerr << " .\n";
  }
}

:

fib(0)=0
fib(1)=1
fib(2)=1
fib(3)=2
fib(4)=3
fib(5)=5
fib(6)=8
fib(7)=13
fib(8)=21
fib(9)=34

__cpp_impl_coroutine 201902L (C++20) ( )
__cpp_lib_coroutine 201902L (C++20) ( )
__cpp_lib_generator 202207L (C++23) std::generator:

, .

(C++23)
view,
( ) []

1.  David Mazires, 2021 - C++20.
2.  Lewis Baker, 2017-2022 - .

Web Proxy Viewer  |  New URL  |  Original Page