gpt4 book ai didi

c++ - 传递给返回 awaitable 的函数的临时对象在使用 co_await 暂停点后是否仍然有效

转载 作者:行者123 更新时间:2023-12-01 14:57:57 25 4
gpt4 key购买 nike

我正在基于 windows io 完成端口的异步套接字类中添加对协程 ts 的支持。
如果没有协程,io 可以这样完成:

sock.async_write(io::buffer(somebuff), [](auto&& ... args){ /* in handler */ });

或者
sock.async_write(std::vector<io::const_buffer>{ ... }, [](auto&& ... args){ /* in handler */ })

其中每个都将返回 void 并将通过处理程序通知结果并且不需要缓存参数,因为操作将在从函数返回时提交

但是对于协程,该函数将返回一个可等待的,在等待它时使用 operator co_await将提交操作,所以我需要在等待中缓存参数以避免使用破坏的临时文件:
awaitable coro_write(const io::const_buffer& buff)
{
return awaitable{ *this, buff };
}

awaitable coro_write(const std::vector<io::const_buffer>& buffs)
{
return awaitable{ *this, buffs };
}


第一个中的拷贝不会造成伤害,但在第二个中它会造成伤害,因为它将触发堆分配并复制 vector 内容。

所以我正在寻找解决方案,并在阅读此页面时 coroutines ts我遇到了这个:

A typical generator's yield_value would store (copy/move or just store the address of, since the argument's lifetime crosses the suspension point inside the co_await) its argument into the generator object and return std::suspend_always, transferring control to the caller/resumer.



并且在同一页面上说 co_yield表达式等价于:
co_await promise.yield_value(expr)

这也类似于:
co_await sock.coro_write(expr)

我打开visual studio 2019自带的generator header,看到里面还存储了参数的地址到 yield_value并稍后通过 generator::iterator::operator *() 检索到它在协程暂停后的调用者站点中:
struct promise_type {
_Ty const* _CurrentValue;
auto yield_value(_Ty const& _Value) {
_CurrentValue = _STD addressof(_Value);
return suspend_always{};
}
}

struct iterator {
_NODISCARD reference operator*() const {
return *_Coro.promise()._CurrentValue;
}
_NODISCARD pointer operator->() const {
return _Coro.promise()._CurrentValue;
}
}

由此我得出结论,传递给函数的参数返回与 co_await 一起使用的等待程序。在协程恢复或销毁之前也将保持有效,对吗?或者这是 yield_value专用的在 promise 类型中?

最佳答案

使用 Visual Studio 2019 16.4.4 进行测试,可以正常工作。事实上,传递的参数和转换后的参数也保持有效并且不会被破坏,直到 await_resume 返回或 await_suspend 由于抛出的异常或指示无意挂起 coroutine 的返回值而未挂起。

这就是我所做的:

1 - 为缓冲区类型创建的析构函数如下:

~const_buffer()
{
std::cout << __FUNCSIG__ << std::endl;
}

~mutable_buffer()
{
std::cout << __FUNCSIG__ << std::endl;
}

// mutable to const conversion

operator const_buffer() const noexcept { return { data_, size_ }; }


2 - 在从 issue_coro_op 调用的 await_suspend 的末尾我放了类似的打印:
void issue_coro_op(awaitable& a)
{
// use the awaitable to issue the op
std::cout << "~ " << __FUNCSIG__ << std::endl;
}

3 - 在 await_resume 的末尾我放了一个类似的打印

4 - 这是传递的缓冲区参数的类型:

awaitable coro_read(const io::mutable_buffer& buff, transfer_flags);

awaitable coro_write(const io::const_buffer& buff, transfer_flags);

template </**/>
awaitable::awaitable(const io::mutable_buffer& buff) { /*store in ptr*/ }

template </**/>
awaitale::awaitable(const io::const_buffer& buff) { /*store in ptr*/ }


5 - 这是 echo 协程:
io::task<> run_session(tcp::async_socket sock)
{
char buff[1024];
string server_msg;
try
{
for (;;)
{
auto n = co_await sock.coro_read(io::buffer(buff), transfer_flags::unspecified);
if (buff[n - 1] == '\n')
--n;
cout << ">> " << string_view{ buff, n } << endl;

server_msg = fmt::format("{{ server message : {} }}\n", string_view{ buff, n });
n = co_await sock.coro_write(io::buffer(server_msg), transfer_flags::unspecified);
}
}
catch (std::exception & ex)
{
cout << "[!] a client has disconnected : " << ex.what() << endl;
}
}

6 - 用 nc 测试:
nc localhost 4567
some message
{ server message : some message }

7 - 服务器输出:
issue_coro_op -> read
await_resume -> read
~mutable_buffer -> read
>> some message
issue_coro_op -> write
await_resume -> write
~const_buffer -> write
~mutable_buffer -> write

注意到 io::buffer 返回 io::mutable_buffer ,它在写操作中被转换为 io::const_buffer 并且两者在恢复之前保持有效,然后它们以相反的顺序被破坏

我无法使用 clang-cl 8 进行测试,因为它在编译代码时崩溃了!和 mingw-w64 与 gcc 8 尚不支持协程

关于c++ - 传递给返回 awaitable 的函数的临时对象在使用 co_await 暂停点后是否仍然有效,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60737075/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com