Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
josuttis_nm_c20_the_complete_guide.pdf
Скачиваний:
44
Добавлен:
27.03.2023
Размер:
5.85 Mб
Скачать

15.5 Exceptions in Coroutines

523

Thus, if you do not need the promise, you can always use the type std::coroutine_handle<void> or the type std::coroutine_handle<> without any template argument. Note that std::coroutine_handle<> does not provide the promise() member function:

void callResume(std::coroutine_handle<> h)

{

h.resume();

// OK

h.promise();

// ERROR: no member promise() provided for h

}

auto hdl = std::coroutine_handle<decltype(prm)>::from_promise(prm);

...

callResume(hdl); // OK: hdl converts to std::coroutine_handle<void>

15.5 Exceptions in Coroutines

When an exception is thrown inside a coroutine and this exception is not locally handled, unhandled_exception() is called in a catch clause behind the coroutine body (see Figure 15.1). It is also called in the case of exceptions from initial_suspend(), yield_value(), return_void(), return_value(), or any awaitable used within the coroutine.

You have the following options for dealing with these exceptions:

• Ignoring the exception

In this case, unhandled_exception() just has an empty body: void unhandled_exception() {

}

• Processing the exception locally

In this case, unhandled_exception() just deals with the exception. Because you are inside a catch clause, you have to rethrow the exception and deal with it locally:

void unhandled_exception() { try {

throw; // rethrow caught exception

}

catch (const std::exception& e) {

std::cerr << "EXCEPTION: " << e.what() << std::endl;

}

catch (...) {

std::cerr << "UNKNOWN EXCEPTION" << std::endl;

}

}

• Ending or aborting the program (e.g., by calling std::terminate()):

void unhandled_exception() {

...

std::terminate();

}

524

Chapter 15: Coroutines in Detail

• Storing the exception for later use with std::current_exception()

In this case, you need an exception pointer in the promise type, which is then set:

struct promise_type { std::exception_ptr ePtr;

...

void unhandled_exception() {

ePtr = std::current_exception();

}

};

You also have to provide a way to process the exceptions in the coroutine interface. For example:

class [[nodiscard]] CoroTask {

 

...

 

bool resume() const {

 

if (!hdl || hdl.done()) {

 

return false;

 

}

 

hdl.promise().ePtr = nullptr;

// no exception yet

hdl.resume();

// RESUME

if (hdl.promise().ePtr) {

// RETHROW any exception from the coroutine

std::rethrow_exception(hdl.promise().ePtr);

}

return !hdl.done();

}

};

Of course, combinations of these approaches are possible.

Note that after unhandled_exception() is called without ending the program, the coroutine is at its end. final_suspend() is called directly and the coroutine is suspended.

The coroutine is also suspended if you throw or rethrow an exception in unhandled_exception(). Any exception thrown from unhandled_exception() is ignored.