【问题标题】:Is it possible to combine coroutines and templates from `<algorithm>` header?是否可以结合`<algorithm>` 标头中的协程和模板?
【发布时间】:2023-02-08 23:47:43
【问题描述】:

我写了很多基于 TCP/IP 的 C++ 软件,我使用现代 C++ 协程进行网络通信。现在假设我有一组 URL,我想找到哪个 URL 下载包含“Hello”字符串的文档:

vector<string> my_urls = { /* URLs list here */ };
auto hello_iterator = find_if(my_urls.begin(), my_urls.end(), [](const string &url) 
{ 
    string downloaded_data = download(url);
    return downloaded_data.find("Hello") != string::npos;
});

这里我们使用同步download(const std::string&amp; url)函数为每个URL下载数据。

对于协程,我想做类似的事情:

vector<string> my_urls = { /* URLs list here */ };
vector<string> downloaded_data;
auto hello_iterator = find_if(my_urls.begin(), my_urls.end(), [](const string &url) -> MyPromiseClass
{ 
    string downloaded_data = co_await async_download(url);
    return downloaded_data.find("Hello") != string::npos;
});

我有 MyPromiseClass async_download(const std::string&amp; url),效果很好,我想用它来异步下载数据。

但是这样的代码无法编译。在 Visual C++ 中,我有以下错误:

错误 C2451:“MyPromiseClass”类型的条件表达式不是 有效的

原因是标准的find_if算法“不知道”协程,只是试图将MyPromiseClass转换为bool

但是,我可以轻松实现find_if 和/或任何其他标准算法的协程版本,只需将其if 语句更改为在调用谓词时使用co_await 并返回 promise 而不是迭代器的语句即可,所以我希望C++标准也应该包含类似的算法?

请告知 C++ 标准中是否有任何版本的 &lt;algorithm&gt; 标头或 boost 支持协程,或者是否有任何方法可以轻松地将“旧”算法从 &lt;aglorithm&gt; 标头转换为支持协程而无需手动重写它们或丑陋的代码首先预先计算值(使用协程)然后在这些预先计算的值上使用算法而不是仅仅等待 lambda 表达式中的数据?

【问题讨论】:

    标签: c++ algorithm c++-coroutine


    【解决方案1】:

    只有协程可以调用co_await,但标准算法不是协程(并且可以争辩说它们不应该是)。这意味着您不能将协程传递给标准算法并期望它等待其结果。

    如果标准算法是协程,您不能只调用它们并获得它们的结果 - 相反,它们都会返回期货或协程类型,您必须在继续之前等待它们,类似于您的 async_download 函数不执行的方式'直接返回std::string,而是某种自定义的未来。结果,标准算法真的很难用在任何地方协程。这是必要的,因为传递给标准算法的任何协程都可以自行挂起,这反过来意味着算法本身必须能够自行挂起,使其成为协程。

    请注意,协程“挂起”意味着协程将其状态保存到其协程框架中,然后回报.如果您需要它在调用堆栈的多个级别上工作,则调用堆栈该部分中的每个函数都必须在开玩笑并且能够早点回来当在线某处的协程决定暂停时。 Coroutines 可以通过co_await 简单地做到这一点,你也可以编写代码通过返回一个未来来手动做到这一点。

    由于标准算法返回普通值,而不是期货,它们不能提前返回,因此不支持暂停。结果,他们不能调用协程。

    您可以做的是先下载数据,然后搜索字符串:

    std::vector<std::string> urls = ...;
    std::vector<MyPromiseClass> downloads;
    
    //Start downloading everything in parallel
    std::transform(urls.begin(), urls.end(),
                   std::back_insert_iterator(downloads), async_download);
    
    std::vector<std::string> data;
    
    //Wait for all downloads
    for (auto& promise: downloads) {
        data.push_back(co_await promise);
    }
    
    auto hello_iterator = std::find_if(data.begin(), data.end(), ...);
    

    如果你愿意,你可以创建一个辅助函数(模板协程)co_awaits多个等待对象并返回它们的结果。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-10-13
      • 2016-02-22
      • 2013-03-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多