Комментарии 16
Спасибо за статью, пример взаимодействия с asio лучше любой синтетики.
Сколько уже копий сломано об эти корутины, но мне до сих пор не понятно. В бусте уже были нормальные корутины, что мешало внести в язык что-то похожее на boost::context? С чего вдруг предлагаемая модель обёрток над промисами лучше, чем корутины со стеком? Вот теперь придумывают, как использовать asio, хотя он уже работал с бустовыми корутинами.
<holywar_mode="on"/>
Сколько уже копий сломано об эти корутины, но мне до сих пор не понятно. В бусте уже были нормальные корутины, что мешало внести в язык что-то похожее на boost::context? С чего вдруг предлагаемая модель обёрток над промисами лучше, чем корутины со стеком? Вот теперь придумывают, как использовать asio, хотя он уже работал с бустовыми корутинами.
<holywar_mode="off"/>
+1
Насколько я понял, «модель оберток» предпочли потому, что Boost.Context не переносимый и требует низкоуровневой реализации для каждой платформы. Возможно, играет роль и то, что Coroutines TS предложена Microsoft'ом.
+1
В бусте были потоки пользовательского режима (=волокна), а не сопрограммы.
0
Да, по поводу преимуществ — тут все просто. Корутину компилятор способен оптимизировать в ноль — а волокно нет.
+1
С do_write тут что-то ужасное сделали. Его нужно было делать нормальной сопрограммой, или вовсе объединить с do_read (единственный смысл разделения этих методов в старом коде я вижу лишь в уменьшении уровня вложенности).
0
Я намеренно не объединял, чтобы показать проблему с расползанием co_await вверх по стеку. Если сделать do_write «нормальной корутиной», тогда придется убрать co_await do_write() в do_read() и вызывать просто do_write(). В результате новое чтение будет начинаться, не дожидаясь окончания записи. У меня есть такой вариант кода, вечером выложу.
0
Если сделать do_write «нормальной корутиной», тогда придется убрать ее из-под co_await в do_read()
Нет, не надо. Надо просто дать ей нормальное возвращаемое значение. Например, для примера подойдет std::experimental::future.
0
Я делал возвращаемое значение std:error_code. Использование std::experimental::future или boost::future — такой подход используется в blogs.msdn.microsoft.com/vcblog/2017/05/19/using-c-coroutines-with-boost-c-libraries, но я хочу этого избежать, потому что тогда будет либо синхронное ожидание (future::wait), либо then(), при использовании которого код уже менее близок к синхронному.
0
А, извиняюсь, перепутал Concurrency TS и Coroutines TS :-) Точно, из Coroutines TS же конкретные типы сто лет назад убрали...
Можно подключить cppcoro и использовать cppcoro::task
Или можно использовать вот такой тип в качестве возвращаемого значения (пишу без проверки потому что под рукой нет компилятора):
template <typename T = void> class lazy_task { // Для void нужна отдельная специализация
std::experimental::coroutine_handle<promise_type> handle;
public:
bool await_ready() { return false; }
bool await_suspend(std::experimental::coroutine_handle<> coro) {
handle.resume();
if (handle.done()) return true;
handle.promise().m_continuation = coro;
return false;
}
T await_resume() {
promise_type promise = handle.promise();
handle.destroy();
if (promise .m_exception)
std::rethrow_exception(promise.m_exception);
else
return promise.m_value;
}
struct promise_type {
T m_value;
std::exception_ptr m_exception;
std::experimental::coroutine_handle<> m_continuation;
lazy_task get_return_object() {
return { std::experimental::coroutine_handle<promise_type>::from_promise(*this) };
}
std::experimental::suspend_always initial_suspend() { return {}; }
std::experimental::suspend_always final_suspend() { return {}; }
void return_value(T value) {
m_value = value;
if (m_continuation) m_continuation.resume();
}
void unhandled_exception() {
m_exception = std::current_exception();
if (m_continuation) m_continuation.resume();
}
};
}
0
UPD: упс, return_value же вызывается до final suspend point, нельзя в нем m_continuation.resume() вызывать! Вот так надо:
auto final_suspend() {
struct awaitable {
std::experimental::coroutine_handle<> m_continuation;
bool await_ready() { return false; }
void await_suspend(std::experimental::coroutine_handle<> coro) {
if (m_continuation) m_continuation.resume();
}
void await_resume() { }
};
return awaitable { m_continuation };
}
0
Чистый мёд!
Спасибо!
0
Как-то слишком много синтаксического мусора.
В Asio можно намного проще корутины использовать. Неявно.
В Asio можно намного проще корутины использовать. Неявно.
boost::asio::spawn(io_service, [socket] (boost::asio::yield_context y)) {
socket.async_read(buffer, y);
socket.async_write(buffer, y);
});
0
Это не те корутины которые Coroutines TS.
0
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Использование Boost.Asio с Coroutines TS